2016-05-17 14 views
0

Ich arbeite an einem Problem, wo ich einen bestimmten Bereich von Zeilen in einer riesigen Textdatei mit Daten aus einem anderen (kleiner, aber immer noch großen) Text ersetzen muss Datei.Bash - Ersetzen kleiner Linien in einer großen Textdatei effizient

Angenommen, file1 hat 10.000 Zeilen und file2 3.000 Zeilen. Ich muss Operationen vom Typ durchführen: extrahiere die Zeilen 901-970 aus Datei2 und füge diese in die Zeilen 8701-8770 von Datei1 ein und ersetze, was vorher da war. In dem Problem, ich arbeite an Datei1 hat 61 Millionen Zeilen und Datei2 18 Millionen. Ich muss diese Operation effizient durchführen, da sie mehrmals ausgeführt wird - am Ende wird der gesamte Inhalt von Datei2 irgendwo in Datei1 sein.

Die beste Lösung, die ich bis jetzt habe, besteht darin, die zwei Dateien in kleine Dateien zu teilen, die jeweils die Anzahl der Zeilen des Blocks haben, die kopiert und ersetzt werden (70 im obigen Beispiel). Dies erwies sich als viel effizienter als eine Kopf-und-Schwanz-Kombination, um Teile der Datei zu extrahieren, aber dennoch müssen Teile der Datei berührt werden, die nicht verändert wurden.

Ich frage mich, ob es eine awk/grep/sed Lösung zu diesem gibt. Das Extrahieren eines Teils von Datei2 ist nicht das Problem, aber ich konnte nicht herausfinden, wie man einen Block von Zeilen von Datei1 ersetzt, ohne die gesamte Datei zu laden.

Danke!

+0

können Sie die Datei nicht unbedingt auf eine gerade Anzahl von Zeilen partitionieren. Zum Beispiel: Datei1_1 (1-900), Datei1_2 (901_970), Datei1_3 (971_) und ähnlich für Datei2. Dann schließe dich den Abschnitten an. File1_1, File2_1, File1_3 usw. Wenn Ihre Abschnitte groß sind, bedeutet dies, dass die Abschnittsanzahl überschaubar ist. – karakfa

+0

sollten Sie klarstellen, wenn Sie buchstäblich nach Zeilennummern verarbeiten möchten, oder wenn dies nur eine Annäherung ist, um uns den Umfang des Problems zu zeigen, aber Sie wirklich nach bestimmten Zeichenfolgen suchen, um zu markieren, wo Ersetzungen auftreten werden. Viel Glück – shellter

+0

@ Karafka das ist auch eine Möglichkeit, aber da ich mehrere Ersetzungen in der gesamten Datei tun muss, würde ich immer noch eine große Anzahl von Abschnitten haben. Vielen Dank. – Albertini

Antwort

1

Das Problem ist, dass Sie eine zufällige Art von Operation (im Gegensatz zu sequenziellen Verarbeitung) zu tun haben, "berühren" die Teile der Datei1, die nicht ändern, und Random-Access für Dateien ist auf der Zeichen/Byte-Ebene, nicht auf der Zeilenebene. Das heißt, wenn die Anzahl Bytes (im Gegensatz zu Zeilen), die in Datei1 ersetzt werden, die gleiche ist wie die Anzahl Bytes kommt von Datei2, könnten Sie es tun (mit fseek und dergleichen). Aber es klingt, als ob das in keiner Weise garantiert ist?

Sie müssen also einen einzigen Durchlauf über Datei1 machen, also optimiert der Schlüssel die Verarbeitung innerhalb der Schleife (über Datei1 Zeilen). Überlegen Sie, ob Sie alle Teile von Datei2 mit einem Durchlauf über Datei1 verarbeiten möchten. (Anstatt mehrere Operationen mit beiden Dateien.)

+0

Tatsächlich entspricht die Anzahl der Bytes, die in Datei1 ersetzt werden, derjenigen aus Datei2, also kann ich eine zufällige Operation verwenden, wie Sie vorgeschlagen haben - ich hatte nicht bemerkt, dass dies eine Möglichkeit in meinem Problem war. Ich benutzte dd beide, um die Blöcke aus file2 zu extrahieren und in file1 zu ersetzen - das ist ungefähr 30% schneller als das Teilen der Datei, wie ich es vorher getan habe. Danke für Ihre Hilfe! – Albertini

+0

@ D.Puetzer Cool! Es wäre interessant zu sehen, dass Sie den vollständigen 'dd'-Befehl erhalten, und damit andere, die zu dieser Frage in der Zukunft kommen, einen Starthilfe für ähnliche Probleme bekommen könnten. –

+0

Fertig, danke nochmal. – Albertini

1

Nach Jeff Y Vorschlag habe ich den DD-Befehl verwendet, um die Ersetzungen effizient auf Byte-Ebene zu tun. I extrahieren zuerst einen Block von file2 Verwendung:

dd if="file2" bs="$bperelem" skip="$start_copy" count=1 of="tmp2" 2> /dev/null 

wo bperelem die Anzahl der Bytes des Blocks ist und start_copy ist die Position, in der es sich befindet. Dann ersetze ich diese in file1 mit dem folgenden:

dd if="tmp2" bs="$bperelem" skip=0 count=1 seek="$start_replace" of="file1" conv=notrunc 2> /dev/null 

Für mein spezielles Problem der Variablen start_copy und start_replace innerhalb einer while-Schleife aktualisiert.

Verwandte Themen