Teilen Sie eine 60 GB große Flachdatei mit Datensätzen auf, die sich gelegentlich über mehrere Zeilen erstrecken

Teilen Sie eine 60 GB große Flachdatei mit Datensätzen auf, die sich gelegentlich über mehrere Zeilen erstrecken

Die manuelle Dateneingabe führt dazu, dass die Datenbank mit Datensätzen überfrachtet wird, die mehrere Zeilenumbruchzeichen enthalten. Gute Datensätze, die in einer riesigen 60 GB großen Flatfile mit nur einer Spalte am Anfang und am Ende durch doppelte Anführungszeichen abgegrenzt sind, sollten sich immer nur über eine Zeile erstrecken, wie hier:

„Mittlerweile liegen vollständige Sequenzen zahlreicher mitochondrialer, vieler prokaryotischer und mehrerer Kerngenome vor.“

Bei fehlerhaften Datensätzen erstrecken sie sich über eine unbestimmte Anzahl von Mehrfachzeilen, etwa wie folgt:

„Derzeitiges Rauchen war stark und umgekehrt assoziiert mit einem hohen Risiko

Muster, nach Anpassung an begleitende Risikofaktoren. Im Vergleich zu nie

Raucher hatten bei aktuellen Rauchern eine signifikant geringere Wahrscheinlichkeit für ein Hochrisiko

Muster. "

Diese mehrzeiligen Datensätze verhindern eine nachfolgende Dateiaufteilung durch den UNIX-Befehl split. splitkann diese mehreren Zeilen nicht intelligent als einen einzigen Datensatz erkennen, und dies kann dazu führen, dass ein einzelner Datensatz in mehrere Dateien aufgeteilt wird. Das folgende Perl ist zu langsam, um diese Zeilen für die fehlerhaften Datensätze dieser riesigen Datei vor dem Aufteilen zusammenzuführen, da $count nach einer Wartezeit von mehr als 2 Stunden nicht gedruckt werden kann.

$file=$ARGV[0];
open(INFO, $file) or die("Could not open $file.");
open(OUT, ">out") or die("Could not open $file.");

$mergedline = "";
$count=0;
foreach $line (<INFO>)  {
    print $count++;
    if ($line =~ /^".*"\n$/) {
                print OUT $line;
                $mergedline = "";
                next;
        } elsif ($line =~ /"\n$/) {
                print OUT $mergedline;
                $mergedline = "";
                next;
        } else {
                chomp $line;
                $mergedline .= $line;
        }
}
close(INFO);

Gibt es einen praktischen UNIX-Befehl, um dieses Problem zu lösen, sodass die Ausgabedatei „sauber“ ist und nur einzeilige Datensätze enthält, die verarbeitet werden können split?

sedscheint eine Option zu sein, aber keiner der folgenden Beiträge beantwortet diese Frage:

https://stackoverflow.com/questions/15758814/mehrere Zeilen in eine Zeile umwandeln, die durch Kommas getrennt ist, perl-sed-awk

https://stackoverflow.com/questions/11290616/sed-conditional-merge-of-multiple-lines

http://www.unix.com/shell-programming-and-scripting/80633-sed-combining-multiple-lines-into-one.html

weil die Muster dieser Beiträge zu regelmäßig und konstant sind.

Antwort1

Verwenden Sie diese Option sed, um nur die geteilten Linien zu verbinden

sed ':a
/".*"$/b
N;s/\n/ /;ba' input >> output

dauert auf meinem System 6 Sekunden für eine 10 MB große Datei. Für 60 GB wären das 10 Stunden.

bbeist etwas schneller

bbe -b '/"/:/"/' -o output -e 'y/\n/ /' input

dauert aber immer noch 4 Sekunden.

Ich fürchte, diese Skriptsprachen sind nicht das richtige Werkzeug, um bei extrem großen Dateien gute Ergebnisse zu erzielen. Wie wäre es, ein kleines Programm in zu schreiben C?

Antwort2

Beispiel mit gawk:

awk 'BEGIN {RS = "\"\n"} {++n; print $0"\""> n".txt"}' input

Dies bedeutet, dass die Datei inputin eine beliebige Sequenz von "gefolgt von einem Zeilenumbruch ( \n) aufgeteilt werden soll. Dadurch werden Zeilenumbrüche ignoriert, die nicht unmittelbar auf ein Anführungszeichen folgen, sodass mehrzeilige Datensätze erhalten bleiben. In diesem Beispiel wird die Ausgabe in eine Textdatei geschrieben, aber wenn Sie den > n".txt"Teil entfernen, können Sie die Datensätze stattdessen an eine Pipeline senden.

Antwort3

Ihr PerlVorgang ist langsam, weil die forSchleife zum Einlesen der Datei verwendet wird. Sie sollten die Schleife wirklich verwenden while, da die forSchleife die gesamte Datei auf einmal in den Speicher lädt. Deshalb dauert das Drucken von $count so lange.

perl -ne '
   print,next if /^".*"$/m or /"$/m;
   chomp, $_ .= <>, redo unless eof;
' gene.data

verwandte Informationen