Come suddividere in modo efficiente un file di testo di grandi dimensioni senza dividere i record multilinea?


9

Ho un grosso file di testo (~ 50Gb quando gz'ed). Il file contiene 4*Nrighe o Nrecord; cioè ogni record è composto da 4 righe. Vorrei dividere questo file in 4 file più piccoli, ognuno dei quali misura circa il 25% del file di input. Come posso dividere il file al limite del record?

Un approccio ingenuo sarebbe quello zcat file | wc -ldi ottenere il conteggio delle righe, dividere quel numero per 4 e quindi utilizzare split -l <number> file. Tuttavia, questo va oltre il file due volte e il line-counte è estremamente lento (36 minuti). Esiste un modo migliore?

Questo si avvicina ma non è quello che sto cercando. La risposta accettata conta anche un conteggio di riga.

MODIFICARE:

Il file contiene i dati di sequenziamento in formato fastq. Due record sembrano così (anonimizzati):

@NxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxGCGA+ATAGAGAG
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxTTTATGTTTTTAATTAATTCTGTTTCCTCAGATTGATGATGAAGTTxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+
AAAAA#FFFFFFFFFFFFAFFFFF#FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF<AFFFFFFFFFFAFFFFFFFFFFFFFFFFFFF<FFFFFFFFFAFFFAFFAFFAFFFFFFFFAFFFFFFAAFFF<FAFAFFFFA
@NxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxGCGA+ATAGAGAG
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxCCCTCTGCTGGAACTGACACGCAGACATTCAGCGGCTCCGCCGCCxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+
AAAAA#FFFFF7FFFFFFAFFFFA#F7FFFFFFFFF7FFFFFAF<FFFFFFFFFFFFFFAFFF.F.FFFFF.FAFFF.FFFFFFFFFFFFFF.)F.FFA))FFF7)F7F<.FFFF.FFF7FF<.FFA<7FA.<.7FF.FFFAFF

La prima riga di ogni record inizia con a @.

EDIT2:

zcat file > /dev/null dura 31 minuti.

EDIT3: Onlye la prima riga inizia con @. Nessuno degli altri lo farà mai. Vedi qui . I registri devono rimanere in ordine. Non va bene aggiungere nulla al file risultante.


Quanto dura un singolo zcat file > /dev/null?
Choroba,

Potete fornire un piccolo esempio del file in questione?
FloHimself

Dici che ogni record inizia con @e anche che ci sono 4 righe per record. Sono entrambi assoluti? - e le linee 2,3,4 possono iniziare con @? e ci sono intestazioni non record delle righe del piè di pagina nel file?
Peter

1
Stai cercando una soluzione che gestisca input compressi e / o produca output compressi? Stai cercando quattro file compressi di dimensioni uguali?
Stephen Kitt,

Risposte:


4

Non penso che tu possa farlo, non in modo affidabile e non nel modo in cui lo chiedi. Il fatto è che il rapporto di compressione dell'archivio probabilmente non sarà distribuito uniformemente dalla testa alla coda - l'algoritmo di compressione si applicherà meglio ad alcune parti rispetto ad altre. Funziona così. E quindi non puoi fattorizzare la tua divisione sulla dimensione del file compresso.

Inoltre, gzipnon supporta l'archiviazione delle dimensioni originali dei file compressi di dimensioni superiori a 4 GB - non può gestirlo. E quindi non puoi interrogare l'archivio per ottenere una dimensione affidabile, perché ti ingannerà.

La cosa a 4 righe - è abbastanza facile, davvero. La cosa da 4 file: non so come poterlo fare in modo affidabile e con una distribuzione uniforme senza prima estrarre l'archivio per ottenere le sue dimensioni non compresse. Non credo che tu possa perché ho provato.

Tuttavia, ciò che puoi fare è impostare una dimensione massima per i file di output divisi e assicurarti che siano sempre rotti alle barriere dei record. Che puoi fare facilmente. Ecco un piccolo script che lo farà estraendo l' gziparchivio e eseguendo il piping del contenuto attraverso alcuni ddpipe buffer specifici con count=$rptargomenti specifici , prima di passarlo attraverso lz4per decomprimere / ricomprimere ogni file al volo. Ho anche lanciato alcuni teetrucchetti per stampare anche le ultime quattro righe per ogni segmento su stderr.

(       IFS= n= c=$(((m=(k=1024)*k)/354))
        b=bs=354xk bs=bs=64k
        pigz -d </tmp/gz | dd i$bs o$b |
        while   read -r line _$((n+=1))
        do      printf \\n/tmp/lz4.$n\\n
        { {     printf %s\\n "$line"
                dd count=$c i$b o$bs
        }|      tee /dev/fd/3|lz4 -BD -9 >/tmp/lz4.$n
        } 3>&1| tail -n4 |tee /dev/fd/2 |
                wc -c;ls -lh /tmp/[gl]z*
        done
)

Continuerà fino a quando non avrà gestito tutti gli input. Non tenta di dividerlo per una percentuale - che non può ottenere - ma invece lo divide per un conteggio di byte non elaborati massimo per divisione. E comunque, una grande parte del tuo problema è che non puoi ottenere una dimensione affidabile sul tuo archivio perché è troppo grande - qualunque cosa tu faccia, non farlo di nuovo - fai le divisioni a meno di 4 g un pezzo in questo giro , può essere. Questo piccolo script, almeno, ti consente di farlo senza dover mai scrivere un byte non compresso su disco.

Ecco una versione più breve ridotta all'essenziale: non aggiunge tutte le cose del rapporto:

(       IFS= n= c=$((1024*1024/354))
        pigz -d | dd ibs=64k obs=354xk |
        while   read -r line _$((n+=1))
        do {    printf %s\\n "$line"
                dd count=$c obs=64k ibs=354xk
        }  |    lz4 -BD -9  >/tmp/lz4.$n
        done
)  </tmp/gz

Fa tutte le stesse cose della prima, per lo più, semplicemente non ha molto da dire al riguardo. Inoltre, c'è meno disordine, quindi è più facile vedere cosa sta succedendo, forse.

Il IFS=punto è solo gestire una readriga per iterazione. Noi readuno perché abbiamo bisogno che il nostro ciclo termini alla fine dell'input. Questo dipende dalla dimensione del record , che, per il tuo esempio, è di 354 byte per. Ho creato un gziparchivio da 4 + gb con alcuni dati casuali per testarlo.

I dati casuali sono stati ottenuti in questo modo:

(       mkfifo /tmp/q; q="$(echo '[1+dPd126!<c]sc33lcx'|dc)"
        (tr '\0-\33\177-\377' "$q$q"|fold -b144 >/tmp/q)&
        tr '\0-\377' '[A*60][C*60][G*60][N*16][T*]' | fold -b144 |
        sed 'h;s/^\(.\{50\}\)\(.\{8\}\)/@N\1+\2\n/;P;s/.*/+/;H;x'|
        paste "-d\n" - - - /tmp/q| dd bs=4k count=kx2k  | gzip
)       </dev/urandom >/tmp/gz 2>/dev/null

... ma forse non devi preoccuparti così tanto, dato che hai già i dati e tutto il resto. Torna alla soluzione ...

Fondamentalmente pigz- che sembra decomprimersi un po 'più velocemente di quanto non faccia zcat- esegue il pipe out del flusso non compresso e ddbuffer che vengono emessi in blocchi di scrittura dimensionati specificamente con un multiplo di 354 byte. Il ciclo sarà readuna $linevolta ogni iterazione di test che ingresso è ancora arrivando, che sarà printfpoi printfa lz4prima di un altro ddè chiamato a leggere blocchi di dimensioni specificamente ad un multiplo di 354 byte - di sincronizzarsi con il buffer dddi processo - per la durata. Ci sarà una breve lettura per iterazione a causa dell'iniziale read $line- ma non importa, perché lo stiamo stampando al lz4nostro processo di raccolta - comunque.

L'ho impostato in modo che ogni iterazione legga circa 1 gb di dati non compressi e comprima tale in-stream a circa 650 Mb circa. lz4è molto più veloce di qualsiasi altro metodo di compressione utile, motivo per cui l'ho scelto qui perché non mi piace aspettare. xzfarebbe comunque un lavoro molto migliore alla compressione effettiva, probabilmente. Una cosa a riguardo lz4, tuttavia, è che spesso può decomprimere a velocità vicine alla RAM, il che significa molte volte che puoi decomprimere un lz4archivio velocemente come potresti scriverlo in memoria comunque.

Quello grande fa alcuni rapporti per iterazione. Entrambi i loop stamperanno ddil rapporto sul numero di byte grezzi trasferiti, sulla velocità e così via. Il grande ciclo stamperà anche le ultime 4 righe di input per ciclo, e un byte conta per lo stesso, seguito da una lsdelle directory in cui scrivo gli lz4archivi. Ecco un paio di round di output:

/tmp/lz4.1
2961+1 records in
16383+1 records out
1073713090 bytes (1.1 GB) copied, 169.838 s, 6.3 MB/s
@NTACGTANTTCATTGGNATGACGCGCGTTTATGNGAGGGCGTCCGGAANGC+TCTCTNCC
TACGTANTTCATTGGNATGACGCGCGTTTATGNGAGGGCGTCCGGAANGCTCTCTNCCGAGCTCAGTATGTTNNAAGTCCTGANGNGTNGCGCCTACCCGACCACAACCTCTACTCGGTTCCGCATGCATGCAACACATCGTCA
+
I`AgZgW*,`Gw=KKOU:W5dE1m=-"9W@[AG8;<P7P6,qxE!7P4##,Q@c7<nLmK_u+IL4Kz.Rl*+w^A5xHK?m_JBBhqaLK_,o;p,;QeEjb|">Spg`MO6M'wod?z9m.yLgj4kvR~+0:.X#(Bf
354

-rw-r--r-- 1 mikeserv mikeserv 4.7G Jun 16 08:58 /tmp/gz
-rw-r--r-- 1 mikeserv mikeserv 652M Jun 16 12:32 /tmp/lz4.1

/tmp/lz4.2
2961+1 records in
16383+1 records out
1073713090 bytes (1.1 GB) copied, 169.38 s, 6.3 MB/s
@NTTGTTGCCCTAACCANTCCTTGGGAACGCAATGGTGTGANCTGCCGGGAC+CTTTTGCT
TTGTTGCCCTAACCANTCCTTGGGAACGCAATGGTGTGANCTGCCGGGACCTTTTGCTGCCCTGGTACTTTTGTCTGACTGGGGGTGCCACTTGCAGNAGTAAAAGCNAGCTGGTTCAACNAATAAGGACNANTTNCACTGAAC
+
>G-{N~Q5Z5QwV??I^~?rT+S0$7Pw2y9MV^BBTBK%HK87(fz)HU/0^%JGk<<1--7+r3e%X6{c#w@aA6Q^DrdVI0^8+m92vc>RKgnUnMDcU:j!x6u^g<Go?p(HKG@$4"T8BWZ<z.Xi
354

-rw-r--r-- 1 mikeserv mikeserv 4.7G Jun 16 08:58 /tmp/gz
-rw-r--r-- 1 mikeserv mikeserv 652M Jun 16 12:32 /tmp/lz4.1
-rw-r--r-- 1 mikeserv mikeserv 652M Jun 16 12:35 /tmp/lz4.2

gzip -lFunziona solo con <2GiB file non compressi IIRC (qualcosa di più piccolo del file OP comunque).
Stéphane Chazelas,

@ StéphaneChazelas - accidenti. Questo è l'unico modo in cui ho potuto immaginare di ottenere una dimensione non compressa. Senza quello, questo non funziona affatto.
Mikeserv,

4

La suddivisione dei file sui limiti dei record è in realtà molto semplice, senza alcun codice:

zcat your_file.gz | split -l 10000 - output_name_

Questo creerà file di output di 10000 linee ciascuno, con nomi output_name_aa, output_name_ab, output_name_ac, ... Con un input grande come il tuo, questo ti darà molti file di output. Sostituisci 10000con un multiplo di quattro e puoi rendere i file di output grandi o piccoli come preferisci. Sfortunatamente, come con le altre risposte, non c'è un buon modo per garantire che otterrai il numero desiderato di (approssimativamente) uguali dimensioni dei file di output senza fare ipotesi sull'input. (O effettivamente eseguendo il piping dell'intero processo wc.) Se i tuoi record hanno dimensioni approssimativamente uguali (o almeno, distribuiti in modo uniformemente uniforme) puoi provare a elaborare una stima come questa:

zcat your_file.gz | head -n4000 | gzip | wc -c

Questo ti dirà la dimensione compressa dei primi 1000 record del tuo file. Sulla base di questo, probabilmente puoi trovare una stima di quante righe vuoi in ogni file per finire con quattro file. (Se non vuoi che un quinto file degenerato rimanga, assicurati di riempire un po 'la tua stima, o preparati a fissare il quinto file sulla coda del quarto.)

Modifica: ecco un altro trucco, supponendo che tu voglia file di output compressi:

#!/bin/sh

base=$(basename $1 .gz)
unpigz -c $1 | split -l 100000 --filter='pigz -c > _$FILE.gz' - ${base}_

batch=$((`ls _*.gz | wc -l` / 4 + 1))
for i in `seq 1 4`; do
  files=`ls _*.gz | head -$batch`
  cat $files > ${base}_$i.gz && rm $files
done

Questo creerà molti file più piccoli e poi li ricollegherà rapidamente. (Potrebbe essere necessario modificare il parametro -l a seconda della lunghezza delle linee nei file.) Si presume che tu abbia una versione relativamente recente di coreutils GNU (per split --filter) e circa il 130% delle dimensioni del tuo file di input in spazio libero su disco. Sostituisci gzip / zcat con pigz / unpigz se non li hai. Ho sentito che alcune librerie di software (Java?) Non sono in grado di gestire i file gzip concatenati in questo modo, ma finora non ho avuto problemi. (pigz usa lo stesso trucco per parallelizzare la compressione.)


Se hai installato pigz, puoi velocizzare leggermente le cose sostituendo "pigz -cd" con "zcat".
Estratto il

2
Ah, ho appena notato che hai già menzionato la divisione nella domanda. Ma davvero, qualsiasi soluzione sta per fare la stessa cosa della divisione sotto il cofano. La parte difficile è capire quante righe devi inserire in ogni file.
Estratto il

3

Da quello che raccolgo dopo aver controllato la sfera di Google e aver testato ulteriormente un .gzfile da 7,8 GiB , sembra che i metadati delle dimensioni del file non compresso originale non siano accurati (cioè errati ) per .gzfile di grandi dimensioni (maggiori di 4GiB (forse 2GiB per alcuni versioni di gzip.)
Re. il mio test sui metadati di gzip:

* The compressed.gz file is  7.8 GiB ( 8353115038 bytes) 
* The uncompressed  file is 18.1 GiB (19436487168 bytes)
* The metadata says file is  2.1 GiB ( 2256623616 bytes) uncompressed

Quindi sembra che non sia possibile determinare la dimensione non compressa senza decomprimerla (il che è un po 'approssimativo, per non dire altro!)

Comunque, ecco un modo per dividere un file non compresso ai limiti del record, in cui ogni record contiene 4 righe .

Utilizza la dimensione del file in byte (via stat) e con il awkconteggio dei byte (non caratteri). Se la fine della linea è LF| CR| CRLF, questo script gestisce la lunghezza finale della riga tramite la variabile incorporata RT).

LC_ALL=C gawk 'BEGIN{"stat -c %s "ARGV[1] | getline inSize
                      segSiz=int(inSize/4)+((inSize%4)==0?0:1)
                      ouSplit=segSiz; segNb=0 }
               { lnb++; bytCt+=(length+length(RT))
                 print $0 > ARGV[1]"."segNb
                 if( lnb!=4 ) next
                 lnb=0
                 if( bytCt>=ouSplit ){ segNb++; ouSplit+=segSiz }
               }' myfile

Di seguito è riportato il test che ho usato per verificare che il conteggio delle righe di ogni file sia mod 4 == 0

for i in myfile  myfile.{0..3}; do
    lc=$(<"$i" wc -l)
    printf '%s\t%s\t' "$i" $lc; 
    (( $(echo $lc"%4" | bc) )) && echo "Error: mod 4 remainder !" || echo 'mod 4 ok'  
done | column -ts$'\t' ;echo

Uscita di prova:

myfile    1827904  mod 4 ok
myfile.0  456976   mod 4 ok
myfile.1  456976   mod 4 ok
myfile.2  456976   mod 4 ok
myfile.3  456976   mod 4 ok

myfile è stato generato da:

printf %s\\n {A..Z}{A..Z}{A..Z}{A..Z}—{1..4} > myfile

2

Questa non è una risposta seria! Ho appena giocato flexe questo probabilmente non funzionerà su un file di input con ~ 50Gb (se non del tutto, su dati di input più grandi rispetto al mio file di test):

Questo funziona per me su un file ~ 1Gb input.txt :

Dato il flexfile di input splitter.l :

%{
#include <stdio.h>
extern FILE* yyin;
extern FILE* yyout;

int input_size = 0;

int part_num;
int part_num_max;
char **part_names;
%}

%%
@.+ {
        if (ftell(yyout) >= input_size / part_num_max) {
            fclose(yyout);
            if ((yyout = fopen(part_names[++part_num], "w")) == 0) {
                exit(1);
            }
        }
        fprintf(yyout, "%s", yytext);
    }
%%

int main(int argc, char *argv[]) {

    if (argc < 2) {
        return 1;
    } else if ((yyin = fopen(argv[1], "r")) == 0) {
        return 1;
    } else if ((yyout = fopen(argv[2], "w")) == 0) {
        fclose(yyin);
        return 1;
    } else {

        fseek(yyin, 0L, SEEK_END);
        input_size = ftell(yyin);
        rewind(yyin);

        part_num = 0;
        part_num_max = argc - 2;
        part_names = argv + 2;

        yylex();

        fclose(yyin);
        fclose(yyout);
        return 0;
    }
}

generare lex.yy.c e compilarlo nel file splitterbinario con:

$ flex splitter.l && gcc lex.yy.c -ll -o splitter

Uso:

$ ./splitter input.txt output.part1 output.part2 output.part3 output.part4

Tempo di esecuzione per input.txt da 1 GB :

$ time ./splitter input.txt output.part1 output.part2 output.part3 output.part4

real    2m43.640s
user    0m48.100s
sys     0m1.084s

Il vero lessico qui è così semplice, davvero non trarrai beneficio dal lex. Basta chiamare getc(stream)e applicare una logica semplice. Inoltre, sai che il. (punto) il carattere regex in (f) lex corrisponde a qualsiasi carattere tranne newline , giusto? Considerando che questi record sono multilinea.
Kaz,

@Kaz Sebbene le tue dichiarazioni siano generalmente corrette, in realtà funziona con i dati forniti in Q.
FloHimself

Solo per caso, perché non esiste una regola predefinita quando non corrisponde nulla: consuma un personaggio e stampalo sull'output! In altre parole d'ordine, puoi fare il cambio del tuo file semplicemente con una regola che riconosce il @carattere, e quindi lasciare che la regola predefinita copi i dati. Ora hai la tua regola che copia parte dei dati come un grande token, e quindi la regola predefinita ottiene la seconda riga un carattere alla volta.
Kaz,

Grazie per il chiarimento. Mi chiedo come risolveresti questo compito txr.
FloHimself

Non sono sicuro che lo farei perché il compito è quello di fare una cosa molto semplice con una grande quantità di dati, il più velocemente possibile.
Kaz,

1

Ecco una soluzione in Python che fa passare il file di input scrivendo i file di output mentre procede.

Una caratteristica dell'uso wc -lè che stai assumendo che tutti i record qui abbiano le stesse dimensioni. Questo può essere vero qui, ma la soluzione qui sotto funziona anche quando non è così. In pratica utilizza wc -co il numero di byte nel file. In Python, questo viene fatto tramite os.stat ()

Quindi ecco come funziona il programma. Per prima cosa calcoliamo i punti di divisione ideali come offset di byte. Quindi leggi le righe del file di input che scrivono nel file di output appropriato. Quando vedi che hai superato il punto di divisione successivo ottimale e sei a un limite di record, chiudi l'ultimo file di output e apri il successivo.

Il programma è ottimale in questo senso, legge una volta i byte del file di input; Ottenere le dimensioni del file non richiede la lettura dei dati del file. Lo spazio di archiviazione necessario è proporzionale alla dimensione di una linea. Ma Python o il sistema presumibilmente hanno buffer di file ragionevoli per accelerare l'I / O.

Ho aggiunto i parametri per quanti file suddividere e quale sia la dimensione del record nel caso in cui si desideri modificare questo in futuro.

E chiaramente questo potrebbe essere tradotto anche in altri linguaggi di programmazione.

Un'altra cosa, non sono sicuro che Windows con il suo crlf gestisca correttamente la lunghezza della linea come sui sistemi Unix-y. Se len () è disattivato da uno qui, spero sia ovvio come regolare il programma.

#!/usr/bin/env python
import os

# Adjust these
filename = 'file.txt'
rec_size = 4
file_splits = 4

size = os.stat(filename).st_size
splits = [(i+1)*size/file_splits for i in range(file_splits)]
with open(filename, 'r') as fd:
    linecount = 0
    i = 0 # File split number
    out = open('file%d.txt' % i, 'w')
    offset = 0  # byte offset of where we are in the file: 0..size
    r = 0 # where we are in the record: 0..rec_size-1
    for line in fd:
        linecount += 1
        r = (r+1) % rec_size
        if offset + len(line) > splits[i] and r == 1 :
            out.close()
            i += 1
            out = open('file%d.txt' % i, 'w')
        out.write(line)
        offset += len(line)
    out.close()
    print("file %s has %d lines" % (filename, linecount))

Non si divide a un limite record. per esempio. La prima suddivisione del file secondario avviene dopo la terza riga con questo inputprintf %s\\n {A..Z}{A..Z}{A..Z}{A..Z}—{1..4}
Peter.O

1

L'utente FloHimself sembrava incuriosito da una soluzione TXR . Eccone uno che utilizza il Lisp TXR incorporato :

(defvar splits 4)
(defvar name "data")

(let* ((fi (open-file name "r"))                 ;; input stream
       (rc (tuples 4 (get-lines fi)))            ;; lazy list of 4-tuples
       (sz (/ (prop (stat name) :size) splits))  ;; split size
       (i 1)                                     ;; split enumerator
       (n 0)                                     ;; tuplecounter within split
       (no `@name.@i`)                           ;; output split file name
       (fo (open-file no "w")))                  ;; output stream
  (whilet ((r (pop rc)))  ;; pop each 4-tuple
    (put-lines r fo) ;; send 4-tuple into output file
    ;; if not on the last split, every 1000 tuples, check the output file
    ;; size with stat and switch to next split if necessary.
    (when (and (< i splits)
               (> (inc n) 1000)
               (>= (seek-stream fo 0 :from-current) sz))
      (close-stream fo)
      (set fo (open-file (set no `@name.@(inc i)`) "w")
           n 0)))
  (close-stream fo))

Appunti:

  1. Per lo stesso motivo, è importante popformare ogni tupla dall'elenco pigro delle tuple, in modo che l'elenco pigro sia consumato. Non dobbiamo conservare un riferimento all'inizio dell'elenco perché la memoria aumenterà man mano che marciamo attraverso il file.

  2. (seek-stream fo 0 :from-current)è il caso no-op di seek-stream, che si rende utile restituendo la posizione corrente.

  3. Performance: non menzionarlo. Utilizzabile, ma non porterà a casa trofei.

  4. Dal momento che controlliamo la dimensione solo ogni 1000 tuple, potremmo semplicemente fare la dimensione della tupla 4000 righe.


0

Se non è necessario che i nuovi file siano blocchi contigui del file originale, è possibile farlo completamente sednel modo seguente:

sed -n -e '1~16,+3w1.txt' -e '5~16,+3w2.txt' -e '9~16,+3w3.txt' -e '13~16,+3w4.txt'

La -nferma dalla stampa ogni riga, e ciascuno dei -escript è essenzialmente facendo la stessa cosa. 1~16corrisponde alla prima riga e ogni 16a riga dopo. ,+3significa abbinare le tre righe successive dopo ognuna di quelle. w1.txtdice scrivere tutte quelle righe nel file 1.txt. Questo sta prendendo ogni 4 ° gruppo di 4 righe e scrivendolo in un file, iniziando dal primo gruppo di 4 righe. Gli altri tre comandi fanno la stessa cosa, ma sono entrambi spostati in avanti di 4 righe e scrivono in un file diverso.

Questo si interromperà in modo orribile se il file non corrisponde esattamente alle specifiche che hai definito, ma altrimenti dovrebbe funzionare come previsto. Non l'ho profilato, quindi non so quanto sarà efficiente, ma sedè abbastanza efficiente nella modifica dello stream.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.