Eine riesige (bis zu 2 GiB) Textdatei von mir enthält ungefähr 100 exakte Duplikate jeder Zeile (in meinem Fall nutzlos, da die Datei eine CSV-ähnliche Datentabelle ist).
Was ich brauche, ist, alle Wiederholungen zu entfernen, während (vorzugsweise, aber dies kann für einen signifikanten Leistungsschub geopfert werden) die ursprüngliche Sequenzreihenfolge beibehalten. Im Ergebnis soll jede Zeile eindeutig sein. Wenn es 100 gleiche Zeilen gäbe (normalerweise sind die Duplikate über die Datei verteilt und werden keine Nachbarn sein), wäre nur eine einzige davon übrig.
Ich habe ein Programm in Scala geschrieben (halte es für Java, wenn du nichts über Scala weißt), um dies zu implementieren. Aber vielleicht gibt es schnellere C-geschriebene native Tools, die dies schneller können?
UPDATE: Die awk '!seen[$0]++' filename
Lösung schien für mich in Ordnung zu sein, solange die Dateien in der Nähe von 2 GiB oder kleiner waren, aber jetzt, da ich eine 8 GiB-Datei bereinigen möchte, funktioniert sie nicht mehr. Auf einem Mac mit 4 GiB RAM und einem 64-Bit-Windows 7-PC mit 4 GiB RAM und 6 GiB Swap scheint es unendlich zu werden. Und ich bin angesichts dieser Erfahrung nicht begeistert davon, es unter Linux mit 4 GiB RAM zu versuchen.
sort -u
wird wahrscheinlich schneller sein.Antworten:
Eine
awk
Lösung von #bash (Freenode):quelle
awk
Version mit 2 Array-Suchen (als erweiterte Erklärung in Gilles Antwort gezeigt): 0m36.132s vs 0m49.958s .. für 50 Millionen Zeilen .. Ich dachte, der Engpass wäre die E / A, aber die zusätzliche Array-Suche ist ... 1 Million Elemente im Array scheinen eine ziemlich erheblicheEs gibt eine einfache (was nicht selbstverständlich ist) Methode, bei der Standarddienstprogramme verwendet werden
sort
, für deren Ausführung nur ein großer Speicher erforderlich ist. In den meisten Implementierungen gibt es spezielle Optimierungen für große Dateien (ein guter externer Sortieralgorithmus). Ein Vorteil dieser Methode besteht darin, dass nur alle Zeilen in Spezialdienstprogrammen durchlaufen werden, nicht jedoch in interpretierten Sprachen.Wenn alle Zeilen mit einem Nicht-Leerzeichen beginnen, können Sie auf einige der Optionen verzichten:
Bei einer großen Anzahl von Duplikaten ist eine Methode, bei der nur eine einzige Kopie jeder Zeile im Speicher gespeichert werden muss, leistungsfähiger. Mit einigem Interpretationsaufwand gibt es dafür ein sehr kurzes awk-Skript (bereits von enzotib gepostet ):
Weniger präzise:
!seen[$0] {print} {seen[$0] += 1}
ZB die aktuelle Zeile drucken, wenn sie noch nicht gesehen wurde, dann denseen
Zähler für diese Zeile inkrementieren (nicht initialisierte Variablen oder Array-Elemente haben den numerischen Wert 0).Bei langen Zeilen können Sie Speicherplatz sparen, indem Sie für jede Zeile nur eine nicht fälschbare Prüfsumme (z. B. einen kryptografischen Auszug) aufbewahren. Bei Verwendung von SHA-1 benötigen Sie beispielsweise nur 20 Byte plus einen konstanten Overhead pro Zeile. Das Berechnen von Digests ist jedoch ziemlich langsam. Diese Methode gewinnt nur, wenn Sie eine schnelle CPU (insbesondere eine mit einem Hardwarebeschleuniger zum Berechnen der Digests) und im Verhältnis zur Dateigröße und den ausreichend langen Zeilen nicht viel Speicher haben. Mit keinem Basisdienstprogramm können Sie eine Prüfsumme für jede Zeile berechnen. Sie müssten den Interpretationsaufwand für Perl / Python / Ruby / ... tragen oder ein spezielles kompiliertes Programm schreiben.
quelle
awk '!seen[$0]++'
dies, dass, wenn awk zwei doppelte Zeilen sieht, die immer erste beibehalten und alle nachfolgenden ignoriert werden? (Oder wird es den letzten behalten?)sort -u
ändert die Reihenfolge. Meine Antwort zeigt Lösungen, bei denen die Reihenfolge erhalten bleibt (die Reihenfolge der ersten Vorkommen, um genau zu sein).Beachten Sie, dass die Ausgabedatei sortiert wird.
quelle
awk
Befehl in anderen Antworten, aber konzeptionell einfach!sort -u
zum Entfernen von Duplikaten während des Sortierens und nicht danach. (Und spart Speicherbandbreite). Dies ist nur dann besser als dieawk
Version, wenn auch Ihre Ausgabe sortiert werden soll. (Das OP zu dieser Frage möchte, dass seine ursprüngliche Bestellung beibehalten wird , daher ist dies eine gute Antwort für einen etwas anderen Anwendungsfall.)Vorausgesetzt, Sie können es sich leisten, so viel wie die nicht duplizierte Datei im Arbeitsspeicher zu behalten (wenn Ihre Daten tatsächlich um den Faktor 100 dupliziert werden, das sollte ungefähr 20 MB + Overhead sein), können Sie dies mit Perl ganz einfach tun.
Dadurch bleibt auch die Reihenfolge erhalten.
Sie können die Anzahl der Vorkommen jeder Zeile aus dem
%dup
Hash extrahieren, wenn Sie dies wünschen, als zusätzlichen Bonus.Wenn Sie es vorziehen
awk
, sollte dies auch so sein (dieselbe Logik wie die Perl-Version, dieselbe Reihenfolge, dieselben Daten, die in derdup
Variablen gesammelt wurden ):quelle
uniq
macht das alles von selbstDa an Ort und Stelle keine andere Antwort zur Verfügung gestellt wurde, ist hier eine:
quelle
GNU Awk 4.0.2
Sie können
uniq
http://www.computerhope.com/unix/uuniq.htm verwendenuniq
meldet oder filtert wiederholte Zeilen in einer Datei heraus.quelle
'uniq' does not detect repeated lines unless they are adjacent.
Sie müssen sie also zuerst sortieren und die Reihenfolge der nicht doppelten Zeilen verlieren.Python One-Liner:
quelle
OrderedDict
Keine der hier aufgeführten Antworten hat auf meinem Mac funktioniert, daher habe ich ein einfaches Python-Skript geschrieben, das für mich funktioniert. Ich ignoriere führende / nachfolgende Leerzeichen und kümmere mich auch nicht um den Speicherverbrauch.
Speichern Sie das oben auf unique.py und führen Sie es folgendermaßen aus:
quelle
Mit bash 4 kann eine Pure-bash-Lösung verwendet werden, die assoziative Arrays nutzt. Hier ist ein Beispiel
quelle
read
Schleifen, um große Textdateien zu verarbeiten. Die Bash muss ein Byte nach dem anderen lesen, um ein Überschießen einer neuen Zeile zu vermeiden. Bash ist auch nicht sehr schnell in der Textverarbeitung im Allgemeinen im Vergleich zu awk. Wenn Sie dies verwenden,read -ra
vermeiden Sie Backslashes in Ihrer Eingabe. Vergessen Sie auch nicht,unset llist
nach der Schleife, wenn Sie dies in eine Shell-Funktion setzen oder interaktiv verwenden.