Ich habe zwei Dateien huge.txt
und small.txt
. huge.txt
hat rund 600 Millionen Zeilen und es ist 14 GB. Jede Zeile enthält vier durch Leerzeichen getrennte Wörter (Token) und schließlich eine weitere durch Leerzeichen getrennte Spalte mit einer Nummer. small.txt
hat 150K Zeilen mit einer Größe von ~ 3M, einem durch Leerzeichen getrennten Wort und einer Zahl.
Beide Dateien werden mit dem Befehl sort ohne zusätzliche Optionen sortiert. Die Wörter in beiden Dateien können Apostrophe (') und Bindestriche (-) enthalten.
Die gewünschte Ausgabe würde alle Spalten aus der huge.txt
Datei und die zweite Spalte (die Nummer) enthalten, aus der small.txt
das erste Wort huge.txt
und das erste small.txt
übereinstimmende Wort stammen .
Meine Versuche unten scheiterten kläglich mit folgendem Fehler:
cat huge.txt|join -o 1.1 1.2 1.3 1.4 2.2 - small.txt > output.txt
join: memory exhausted
Was ich vermute ist, dass die Sortierreihenfolge irgendwie nicht stimmt, obwohl die Dateien vorsortiert sind mit:
sort -k1 huge.unsorted.txt > huge.txt
sort -k1 small.unsorted.txt > small.txt
Die Probleme scheinen bei Wörtern mit Apostrophen (') oder Bindestrichen (-) aufzutreten. Ich habe auch versucht, das Wörterbuch mit der -d
Option zu sortieren, die am Ende auf denselben Fehler stößt.
Ich habe versucht, die Dateien in MySQL zu laden, Indizes zu erstellen und sie zu verbinden, aber auf meinem Laptop scheint es Wochen zu dauern. (Ich habe keinen Computer mit mehr Speicher oder schneller Festplatte / SSD für diese Aufgabe)
Ich sehe zwei Möglichkeiten, weiß aber nicht, wie ich eine davon implementieren soll.
Wie sortiere ich die Dateien so, dass der Befehl join sie für richtig sortiert hält?
Ich dachte daran, MD5 oder andere Hashes der Strings zu berechnen , um die Apostrophe und Bindestriche zu entfernen, aber die Zahlen am Ende der Zeilen intakt zu lassen. Führen Sie das Sortieren und Verbinden mit den Hashes anstelle der Zeichenfolgen selbst durch und "übersetzen" Sie die Hashes schließlich wieder in Zeichenfolgen. Da es nur 150K Hashes geben würde, ist es nicht so schlimm. Was wäre ein guter Weg, um einzelne Hashes für jede der Zeichenfolgen zu berechnen? Etwas AWK-Magie?
Siehe Dateibeispiele am Ende.
Beispiel von vast.txt
had stirred me to 46
had stirred my corruption 57
had stirred old emotions 55
had stirred something in 69
had stirred something within 40
Beispiel für small.txt
caley 114881
calf 2757974
calfed 137861
calfee 71143
calflora 154624
calfskin 148347
calgary 9416465
calgon's 94846
had 987654
Gewünschte Ausgabe:
had stirred me to 46 987654
had stirred my corruption 57 987654
had stirred old emotions 55 987654
had stirred something in 69 987654
had stirred something within 40 987654
Antworten:
IMO der beste Weg, dies zu tun, wäre die Verwendung der Programmier- / Skriptsprache, die Sie am besten kennen und:
quelle
Um auf Michael Borgwardts Antwort aufzubauen: Solange beide Dateien sortiert sind, können Sie sie zusammenfügen, indem Sie im Grunde einen Schritt eines Mergesorts ausführen. Es unterscheidet sich ein wenig von Standard-Mergesort, da Sie nur eine der Dateien behalten möchten. Dies muss natürlich in Ihrer bevorzugten Programmiersprache implementiert werden.
Hier ist eine Skizze des Algorithmus:
Hier ist eine Python-Version (da Python genau das ist, was ich am besten kenne, nicht unbedingt die beste Sprache für den Job):
und der Vollständigkeit halber habe ich mir nach einigem Graben Folgendes für Awk ausgedacht:
Rufen Sie als auf
awk -f program.awk <file1
.quelle
Meine Antwort ähnelt der von Michael Borgwardt, aber Sie müssen nicht alle Dateien in den Speicher laden. Wenn beide Dateien sortiert sind, gehen Sie zeilenweise durch die erste Datei und durchsuchen die zweite Datei binär, um die betreffende Zielzeile zu finden. Das ist viel HD-Zugriff, aber es ist ein geringer Speicherverbrauch.
quelle
Ich weiß, dass es peinlich einfach ist, aber es funktioniert.
Ausgehend von der Annahme, dass meine Originaldateien nur Kleinbuchstaben enthalten, habe ich einfach die problematischen Apostrophe und Bindestriche durch zwei Großbuchstaben ersetzt, die neu sortiert und dann zusammengefügt wurden, und schließlich die Buchstaben wieder in die Zeichen geändert. Das ist es.
Nochmals vielen Dank für alle, die eine Antwort oder einen aufschlussreichen Kommentar beigesteuert haben.
Das Sortieren dauerte für riesig.txt (14Gig) ungefähr 2 Stunden, die Verbindung dauerte weniger als eine Stunde.
quelle
OK, dieser Ansatz verwendet http://cr.yp.to/cdb.html , um den Inhalt von 'small.txt' schneller nachzuschlagen:
cdbmake
(Teil des 'freecdb'-Pakets in Ubuntu, aber es sind viele Implementierungen verfügbar.Verwenden Sie awk, um small.txt an zu leiten
cdbmake
.(Dies transformiert eine Zeile von 'small.txt' von etwas wie "Schlüsselwert" in "+ ks, vs: Schlüssel-> Wert".)
Jetzt gehen Sie Zeile für Zeile über 'vast.txt' und drucken es aus, wobei Sie das erste Wort in 'small.cdb' nachschlagen:
Sie müssten natürlich python-cdb installieren, damit dieses winzige Snippet funktioniert (und es funktioniert aufgrund des ' bedingten Ausdrucks ' nur für Python 2.5. Auf jeden Fall gibt es viele Bindungen für jede Sprache, die Sie mögen. Sie können sie auch verwenden
cdbget
(ein Befehlszeilen-Tool) und rufen Sie es immer wieder auf, aber das Erstellen eines neuen Prozesses für Millionen von Zeilen ist etwas ineffektiv.Wie auch immer, denken Sie daran:
quelle
Anstelle von MySQL können Sie auch PostgreSQL ausprobieren, das diese Aufgabe wahrscheinlich besser bewältigen kann. Lesen Sie den Leitfaden zum effizienten Auffüllen einer Datenbank.
quelle