2010-08-24 12 views
7

Ho più di 125 file TSV di ~ 100 Mb ciascuno che voglio unire. L'operazione di fusione è consentita per distruggere i 125 file, ma non i dati. Che importa è che alla fine, finisco con un grosso file del contenuto di tutti i file uno dopo l'altro (senza un ordine specifico).Come unire file giganteschi in modo efficiente con C#

Esiste un modo efficace per farlo? Mi chiedevo se Windows fornisce un'API per creare semplicemente una grande "Unione" di tutti quei file? Altrimenti dovrò leggere tutti i file e scriverne uno grande.

Grazie!

+0

PS: date un'occhiata qui (possibile duplicato): http://stackoverflow.com/questions/444309/what-would-be-the-fastest-way- to-concatenate-three-files-in-c – Abel

risposta

17

Quindi "unire" è in realtà solo la scrittura dei file uno dopo l'altro? È piuttosto semplice: basta aprire un flusso di output e quindi aprire ripetutamente un flusso di input, copiare i dati, chiudere. Per esempio:

static void ConcatenateFiles(string outputFile, params string[] inputFiles) 
{ 
    using (Stream output = File.OpenWrite(outputFile)) 
    { 
     foreach (string inputFile in inputFiles) 
     { 
      using (Stream input = File.OpenRead(inputFile)) 
      { 
       input.CopyTo(output); 
      } 
     } 
    } 
} 

che sta utilizzando il metodo Stream.CopyTo che è nuova in .NET 4. Se non si sta utilizzando .NET 4, un altro metodo di supporto sarebbe venuto in aiuto:

private static void CopyStream(Stream input, Stream output) 
{ 
    byte[] buffer = new byte[8192]; 
    int bytesRead; 
    while ((bytesRead = input.Read(buffer, 0, buffer.Length)) > 0) 
    { 
     output.Write(buffer, 0, bytesRead); 
    } 
} 

Non c'è niente che sono consapevole che è più efficiente di questo ... ma, soprattutto, questo non occuperà affatto molta memoria sul tuo sistema. Non è come se leggesse ripetutamente l'intero file in memoria, quindi lo scrive di nuovo.

MODIFICA: come indicato nei commenti, ci sono modi in cui è possibile armeggiare con le opzioni di file su potenzialmente rendendolo leggermente più efficiente in termini di ciò che il file system fa con i dati. Ma fondamentalmente leggerete i dati e li scrivete, un buffer alla volta, in entrambi i casi.

+0

Immagino che la tua risposta alla domanda sia no? –

+0

@Marcus: Credo di sì ... anche se non ero sicuro che l'OP sarebbe stato comodo scrivere le versioni di streaming sopra. –

+0

Grazie Jon per l'aiuto! :) Non sapevo di "CopyTo". – Martin

2

farlo dalla riga di comando:

copy 1.txt+2.txt+3.txt combined.txt 

o

copy *.txt combined.txt 
+1

Ti rendi conto che ha detto ** file ** 125 **, giusto? Sarà molto lungo e noioso da digitare. Se hai dato un programma C# per generare la stringa di copia, questa potrebbe essere una * parziale * risposta. – Aaronaught

+6

Amico, quindi usa la seconda opzione, con la maschera del file. Oppure fai un comando dir (cioè dir/b per ottenere solo nomi di file), cattura i nomi dei file in un file e costruisci il comando in un buon editor di testo. Ci sono molti modi per evitare di digitare 125 nomi di file. –

+0

Il punto è che non sei nemmeno arrivato vicino a rispondere alla domanda. Hai fatto un sacco di ipotesi sul dominio del problema che non puoi sapere. Va bene * chiedere * per maggiori dettagli sul dominio, ma non assumere semplicemente che l'autore della domanda abbia scelto un modo non corretto di risolvere il suo problema. -1 per la tua soluzione forse irrilevante e il tuo tono polemico, "amico". – Aaronaught

2

Vuoi dire con merge che si vuole decidere con una certa logica personalizzata quali linee vanno dove? O vuoi dire che vuoi principalmente concatenare i file in uno solo?

Nel caso di quest'ultimo, è possibile che non hai bisogno di fare questo a livello di codice a tutti, basta creare un file batch con questo (/b è per binario, rimuovere se non necessario):

copy /b "file 1.tsv" + "file 2.tsv" "destination file.tsv" 

Usando C#, adotterei il seguente approccio. Scrivere una funzione semplice che le copie di due corsi d'acqua:

void CopyStreamToStream(Stream dest, Stream src) 
{ 
    int bytesRead; 

    // experiment with the best buffer size, often 65536 is very performant 
    byte[] buffer = new byte[GOOD_BUFFER_SIZE]; 

    // copy everything 
    while((bytesRead = src.Read(buffer, 0, buffer.Length)) > 0) 
    { 
     dest.Write(buffer, 0, bytesRead); 
    } 
} 

// then use as follows (do in a loop, don't forget to use using-blocks) 
CopStreamtoStream(yourOutputStream, yourInputStream); 
+0

@Aaronaught: ero a metà strada quando ho presentato, poi ho scritto la seconda parte. Ma anche, nota il piccolo suggerimento nel secondo para: * "genera solo un file batch" *. Generando, intendo: creare automaticamente. Ma poi ho deciso di aggiungere il codice C# :) – Abel

Problemi correlati