2012-03-30 4 views
8

Ich habe CSV-Dateien, die mehrere Spalten haben, die sortiert sind. Zum Beispiel könnte ich Linien wie folgt haben:Split große CSV-Textdatei basierend auf Spalte Wert

19980102,,PLXS,10032,Q,A,,,15.12500,15.00000,15.12500,2 
19980105,,PLXS,10032,Q,A,,,14.93750,14.75000,14.93750,2 
19980106,,PLXS,10032,Q,A,,,14.56250,14.56250,14.87500,2 
20111222,,PCP,63830,N,A,,,164.07001,164.09000,164.12000,1 
20111223,,PCP,63830,N,A,,,164.53000,164.53000,164.55000,1 
20111227,,PCP,63830,N,A,,,165.69000,165.61000,165.64000,1 

Ich möchte die Datei basierend auf der dritten Spalte z. Setzen Sie PLXS- und PCP-Einträge in ihre eigenen Dateien namens PLXS.csv und PCP.csv. Da die Datei vorsortiert ist, befinden sich alle PLXS-Einträge vor den PCP-Einträgen und so weiter.

Normalerweise mache ich solche Dinge in C++, da das die Sprache ist, die ich am besten kenne, aber in diesem Fall ist meine Eingabe CSV-Datei mehrere Gigabyte und zu groß, um in C++ geladen zu werden.

Kann jemand zeigen, wie dies erreicht werden kann? Perl/Python/php/bash-Lösungen sind alle in Ordnung, sie müssen nur in der Lage sein, die große Datei ohne übermäßigen Speicherverbrauch zu handhaben.

+0

haben Sie um überhaupt durchsucht? einige verwandte Fragen auf dieser Seite in allen oben genannten Sprachen und mehr. Sie können suchen: 'site: stackoverflow.com csv nach Wert geteilt 'oder eine solche Variante. viel Glück – bernie

Antwort

1

C++ ist in Ordnung, wenn Sie es am besten wissen. Warum sollten Sie versuchen, die gesamte Datei trotzdem in den Speicher zu laden?

Da die Ausgabe von der gelesenen Spalte abhängt, können Sie problemlos Puffer für Ausgabedateien speichern und den Datensatz während der Verarbeitung in die entsprechende Datei stopfen. Der Speicherbedarf wird dabei relativ gering gehalten.

Ich mache dies (wenn auch in Java), wenn Sie massive Auszüge aus einer Datenbank nehmen müssen. Die Datensätze werden in einen Dateipuffer-Stream geschoben und alles im Speicher wird bereinigt, so dass der Footprint des Programms niemals über das hinausgeht, wozu er anfänglich startet.

Fly durch den Sitz der Hose Pseudo-Code:

  1. Erstellen Sie eine Liste der Ausgabedatei Puffer
  2. öffnen Stream auf Datei zu halten und beginnen zu einem Zeitpunkt, in dem Inhalt einer Zeile lesen
  3. Sind wir auf einen Datensatz gestoßen, der einen offenen Dateistream für seinen Inhaltstyp aufweist?
    • Ja -
      • Erhalten Sie die gespeicherte Datei-Stream
      • speichern den Datensatz in die Datei
      • flush der Strom
    • Nein -
      • einen Stream erstellen und speichern Sie es auf unsere Liste der Ströme
      • speichern Sie die Aufzeichnung auf dem Strom
      • bündig der Strom
  4. Rinse Repeat ...

Grundsätzlich Fortsetzung dieser Verarbeitung, bis wir am Ende der Datei sind.

Da wir nie mehr als Zeiger auf die Streams speichern und wir spülen, sobald wir in die Streams schreiben, haben wir nie etwas im Speicher der Anwendung gespeichert, außer einem Datensatz aus der Eingabedatei. Somit wird der Fußabdruck verwaltet.

+2

+1: C++ ist nicht das Problem. Das Problem besteht darin, die gesamte Datei in den Speicher zu laden. –

26

Hier ist eine alte Schule Motto für Sie (ersetzen Sie einfach die >> mit > die Ausgabedateien jeden Lauf trunkieren):

awk -F, '{print >> ($3".csv")}' input.csv 

Due zu der populären Nachfrage (und einem Juckreiz, den ich gerade hatte), habe ich auch eine Version geschrieben, die die Kopfzeilen in alle Dateien kopiert:

awk -F, '{fn=$3".csv"} NR==1 {hdr=$0} NR>1&&!($3 in p) {p[$3]; print hdr > fn} NR>1 {print >> fn}' input.csv 

Aber man konnte nur mit diesem beginnen und mit dem ersten awk beenden:

HDR=$(head -1 input.csv); for fn in $(tail -n+2 input.csv | cut -f3 -d, | sort -u); do echo $HDR > $fn.csv; done 

Die meisten modernen Systeme haben die awk binär enthalten, aber wenn Sie es nicht haben, können Sie eine exe finden bei Gawk for Windows

+0

das ist genial :) es wäre noch besser, wenn wir die Header behalten könnten –

+1

Es gab keine Header im Original. Vielleicht können Sie eine andere Frage stellen? –

0

Wenn die ersten drei Spalten der Datei nicht zitiert Komma haben, eine einfache Einzeiler ist:

cat file | perl -e 'while(<>){@a=split(/,/,$_,4);$key=$a[2];open($f{$key},">$key.csv") unless $f{$key};print {$f{$key}} $_;} for $key (keys %f) {close $f{$key}}' 

Es ist nicht viel Speicher verbraucht (nur die Asso ciations distinct (3rd_column) -> file-handle werden gespeichert und die Zeilen können in beliebiger Reihenfolge stehen.

Wenn die Spalten komplexer sind (z. B. in Anführungszeichen), verwenden Sie Text::CSV.

+0

tatsächlich, ich merke gerade, dass dies im Wesentlichen die gleiche Antwort ist wie die von Sean Summers unten. –

1
perl -F, -ane '`echo $_ >> $F[2].csv`' < file 

Diese Befehlszeilenoptionen verwendet werden:

  • -n Schleife um jede Zeile der Eingabedatei
  • -l Zeilenumbrüche vor der Verarbeitung entfernt, und fügt sie in danach zurück
  • -a Autosplit Modus - Teilt die Eingangsleitungen in das Array . Standardmäßig wird auf Leerzeichen aufgeteilt.
  • -e führen Sie den Perl-Code
  • -F Autosplit Modifikator, in diesem Fall auf , spaltet

@F ist die Anordnung der Wörter in jeder Zeile, indiziert, beginnend mit $F[0]


Wenn Sie Möchten Sie den Header beibehalten, ist ein komplizierterer Ansatz erforderlich.

perl splitintofiles.pl file

Inhalt von splitintofiles.pl:

open $fh, '<', $ARGV[0]; 
while ($line = <$fh>) { 
    print $line; 
    if ($. == 1) { 
     $header = $line; 
    } else { 
     # $fields[2] is the 3rd column 
     @fields = split /,/, $line; 
     # save line into hash %c 
     $c{"$fields[2].csv"} .= $line; 
    } 
} 
close $fh; 
for $file (keys %c) { 
    print "$file\n"; 
    open $fh, '>', $file; 
    print $fh $header; 
    print $fh $c{$file}; 
    close $fh; 
} 

Eingang:

a,b,c,d,e,f,g,h,i,j,k,l 
19980102,,PLXS,10032,Q,A,,,15.12500,15.00000,15.12500,2 
19980105,,PLXS,10032,Q,A,,,14.93750,14.75000,14.93750,2 
19980106,,PLXS,10032,Q,A,,,14.56250,14.56250,14.87500,2 
20111222,,PCP,63830,N,A,,,164.07001,164.09000,164.12000,1 
20111223,,PCP,63830,N,A,,,164.53000,164.53000,164.55000,1 
20111227,,PCP,63830,N,A,,,165.69000,165.61000,165.64000,1 

Ausgang PCP.csv

a,b,c,d,e,f,g,h,i,j,k,l 
20111222,,PCP,63830,N,A,,,164.07001,164.09000,164.12000,1 
20111223,,PCP,63830,N,A,,,164.53000,164.53000,164.55000,1 
20111227,,PCP,63830,N,A,,,165.69000,165.61000,165.64000,1 

Ausgang PLXS.csv

a,b,c,d,e,f,g,h,i,j,k,l 
19980102,,PLXS,10032,Q,A,,,15.12500,15.00000,15.12500,2 
19980105,,PLXS,10032,Q,A,,,14.93750,14.75000,14.93750,2 
19980106,,PLXS,10032,Q,A,,,14.56250,14.56250,14.87500,2