Ersetzen von Zeichenfolgen in einer sehr großen Datei

10

Ich habe eine sehr lange Reihe von URLs ohne Trennzeichen im gleichen Format wie unten:

http://example.comhttp://example.nethttp://example.orghttp://etc...

Ich möchte, dass jede URL in einer neuen Zeile steht. Ich habe versucht, dies zu tun, indem ich alle Instanzen von "http: //" durch "\ nhttp: //" mit sed ersetzt habe

sed 's_http://_\nhttp://_g' urls.txt

Es tritt jedoch ein Segmentierungsfehler auf (Speicherverletzung). Ich kann nur vermuten, dass die schiere Größe der Datei (über 100 GB) dazu führt, dass sed ein bestimmtes Limit überschreitet.

Ich könnte die Datei zur Verarbeitung in mehrere kleinere Dateien aufteilen, aber alle Instanzen von "http: //" müssten intakt bleiben.

Gibt es einen besseren Weg, dies zu tun?

C Sawyer
quelle
Ich denke, sed mag die 100 GB ohne Zeilenenden nicht, da es versucht, eine einzelne Zeile in seinem Puffer zu lesen.
Jippie
Aufteilen (unabhängig davon, "wo" der Schnitt stattfindet), Verarbeiten und erneutes Zusammensetzen sollten jedoch das richtige Ergebnis liefern.
Enzotib
3
Wenn Sie wirklich eine 100-GB-Textdatei mit einer einzigen langen Zeile haben, ist es besser, ein schnelles C-Programm zu schreiben, um die Arbeit zu erledigen.
fpmurphy

Antworten:

11

Mit können awkSie vermeiden, große Textmengen gleichzeitig zu lesen:

awk -vRS='http://' -vORS='\nhttp://' 1 urls.txt > urlsperline.txt

Der Erfolg kann von der verwendeten awkImplementierung abhängen . Zum Beispiel gawkfunktioniert gut, mawkstürzt aber ab.

Mann bei der Arbeit
quelle
6

Dies wird den Job machen:

perl -pe 'BEGIN { $/ = "//" } s!(?=http://\z)!\n!' urls.txt

Durch das Setzen von $ / habe ich die Definition einer Zeile so geändert, dass sie mit //einer neuen Zeile endet . Dadurch liest Perl jeweils eine URL. Es ist unwahrscheinlich, dass eine URL //außer nach dem Schema enthält, aber wenn dies der Fall ist, verhindert die Regex, dass falsche Zeilen hinzugefügt werden.

Wenn Sie vermeiden möchten, vor der ersten URL eine Leerzeile einzufügen:

perl -pe 'BEGIN { $/ = "//"; print scalar <> } s!(?=http://\z)!\n!' urls.txt

Sie können versuchen, ein Benchmarking durchzuführen, um festzustellen, ob s!http://\z!\nhttp://!es schneller ist. Sie sind gleichwertig. Beachten Sie, dass das /gFlag bei der Ersetzung nicht erforderlich ist, da es nur eine Übereinstimmung pro "Zeile" geben kann.

cjm
quelle
Ist die Perl-Regexp-Engine mit mehreren Gigabyte langen Leitungen in Ordnung?
Alexios
2
@Alexios, wahrscheinlich nicht, aber es muss nicht sein. Da ich mich geändert habe $/, wird immer nur eine URL behandelt.
CJM
Ah, ich sehe, was du dort getan hast. Es ist eine Weile her seit den 90ern, und ich musste man perlvar, aber es macht so Sinn.
Alexios
Unter Linux können URLs mehrere Schrägstriche in Pfade eingebettet haben, sodass dieser Code möglicherweise fehlschlägt, wenn Sie einen davon haben. Das Testen der gesamten Zeichenfolge, http und aller, hat dieses Problem nicht.
Joe
@ Joe, ich teste für den http:Teil in der Regex. Es wird jeden untersuchen //, aber es wird keine neue Zeile hinzugefügt, es sei denn, es findet http://.
cjm
5
  1. Ändern Sie alle Vorkommen von a :mit einem Zeilenumbruch, um die Datei zu zerlegen.
  2. Ersetzen
    • http am Ende der Zeile mit
    • eine neue Zeile gefolgt von http:und fügen Sie die nächste Zeile hinzu
  3. Einmal wiederholen, damit gerade und ungerade Zeilen aktualisiert werden

Diese Schritte sehen folgendermaßen aus:

tr ':' '\n' | sed -e '/http$/{N;s/http\n/\nhttp:/}' | sed -e '/http$/{N;s/http\n/\nhttp:/}'
  1. Überprüfen Sie, ob es Zeilen gibt, die nicht mit beginnen http://, und drucken Sie die Zeilennummern. Dies würde nur auftreten, wenn sich a: irgendwo in der URL befindet, außer nach dem http.

    grep -nv '^http://'

Jippie
quelle