Wie entferne ich doppelte Zeilen in einer Textdatei?

Eine riesige (bis zu 2 GiB) Textdatei von mir enthält ungefähr 100 exakte Duplikate jeder Zeile darin (in meinem Fall unbrauchbar, wie die Datei ist eine CSV-ähnliche Datentabelle).

Ich muss alle Wiederholungen entfernen, während (vorzugsweise, aber dies kann für eine signifikante Leistungssteigerung geopfert werden) die ursprüngliche Sequenzreihenfolge beibehalten wird. Im Ergebnis soll jede Zeile eindeutig sein. Wenn es 100 gleiche Zeilen gäbe (normalerweise sind die Duplikate über die Datei verteilt und keine Nachbarn), gibt es nur noch eine solche.

Ich habe ein Programm in Scala geschrieben (bedenken Sie es Java, wenn Sie nicht über Scala Bescheid wissen), um dies zu implementieren. Aber vielleicht gibt es schnellere native C-Tools, die dies schneller können?

UPDATE: Die awk "!seen[$0]++" filename -Lösung schien für mich einwandfrei zu funktionieren, solange die Dateien waren in der Nähe von 2 GiB oder kleiner, aber jetzt, da ich eine 8 GiB-Datei bereinigen soll, funktioniert sie nicht mehr. Auf einem Mac mit 4 GiB RAM und einem 64-Bit-Windows 7-PC mit 4 GiB RAM scheint es unendlich zu werden und 6 GiB Swap hat einfach nicht mehr genügend Speicher. Und ich bin angesichts dieser Erfahrung nicht begeistert davon, es unter Linux mit 4 GiB RAM zu versuchen.

Kommentare

  • Dies wird Ihre Bestellung zerstören, aber wenn Sie sort -u ausprobiert haben, habe ich keine Ahnung, wie oder ob es auf einer so massiven Datei ausgeführt werden kann.
  • C ist oft nicht wesentlich schneller als Java, und wenn Sie ‚ führen es jetzt (in der richtigen Reihenfolge) aus. ‚ hat eine faire Chance, dass es ‚ ll beenden, bevor Sie hier eine Antwort erhalten, implementieren Sie es und es wird beendet; sort -u ist wahrscheinlich schneller.

Antwort

Eine awk -Lösung, die auf #bash (Freenode) angezeigt wird:

awk "!seen[$0]++" filename 

Kommentare

  • Ich habe es gerade mit einer 2G-Datei versucht und es dauerte drei Minuten auf meinem Notebook. Nicht schlecht. Ich habe auch versucht, uniq Dateiname | awk ‚! gesehen [$ 0] ++ ‚, aber es war ‚ t any schneller.
  • @HashWizard: Dieser Befehl sortiert nicht, eliminiert jedoch jedes nächste Vorkommen derselben Zeile.
  • Sie fragen sich, wie dieser Befehl funktioniert? – Siehe hier: unix.stackexchange.com/questions/159695/how-does-awk-a0-work
  • @MaxWilliams yes Es funktioniert, wenn sie zufällig verteilt sind.
  • Beibehalten von Zeilenumbrüchen oder Zeilen mit Leerzeichen awk '/^\s*?$/||!seen[$0]++'

Antwort

Es gibt eine einfache (was nicht offensichtlich ist) Methode unter Verwendung von Standarddienstprogrammen, die keinen großen Speicher benötigt, außer um sort, das in den meisten Implementierungen spezifische Optimierungen für große Dateien aufweist (ein guter externer Sortieralgorithmus). Ein Vorteil dieser Methode besteht darin, dass nur alle Zeilen in speziellen Dienstprogrammen durchlaufen werden, niemals in interpretierten Sprachen.

<input nl -b a -s : | # number the lines sort -t : -k 2 -u | # sort and uniquify ignoring the line numbers sort -t : -k 1n | # sort according to the line numbers cut -d : -f 2- >output # remove the line numbers 

Wenn alle Zeilen mit a beginnen Nicht-Leerzeichen, können Sie auf einige der folgenden Optionen verzichten:

<input nl | sort -k 2 -u | sort -k 1n | cut -f 2- >output 

Bei einer großen Menge an Duplikaten ist eine Methode erforderlich, bei der nur eine einzige Kopie von gespeichert werden muss Jede Zeile im Speicher bietet eine bessere Leistung. Mit etwas Interpretationsaufwand gibt es „ein sehr prägnantes awk-Skript dafür (bereits gepostet von enzotib ):

<input awk "!seen[$0]++" 

Weniger präzise: !seen[$0] {print} {seen[$0] += 1}, dh drucken Sie die aktuelle Zeile, wenn sie noch nicht gesehen wurde, und erhöhen Sie dann die seen Zähler für diese Zeile (nicht initialisierte Variablen oder Array-Elemente haben den numerischen Wert 0).

Bei langen Zeilen können Sie Speicher sparen, indem Sie nur eine nicht fälschbare Prüfsumme (z. B. einen kryptografischen Digest) jeder Zeile beibehalten . 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 wird nur gewinnen, wenn Sie eine schnelle CPU (insbesondere eine mit einem Hardwarebeschleuniger zum Berechnen der Digests) und nicht viel Speicher im Verhältnis zur Größe der Datei und ausreichend lange Zeilen haben. Mit keinem grundlegenden Dienstprogramm können Sie eine Prüfsumme für jede Zeile berechnen. Sie müssten den Interpretationsaufwand von Perl / Python / Ruby / … tragen oder ein dediziertes kompiliertes Programm schreiben.

<input perl -MDigest::MD5 -ne "$seen{Digest::MD5::md5($_)}++ or print" >output 

Kommentare

  • @Gilles Basierend auf Ihrer Erklärung von awk '!seen[$0]++' bedeutet dies, dass awk, wenn es zwei doppelte Zeilen sieht, die immer erste behält und alle ignoriert nachfolgende? (Oder wird die letzte beibehalten?)
  • @ user779159 Die erste wird beibehalten: Jede Eingabezeile wird entweder sofort gedruckt (erstes Auftreten) oder überhaupt nicht (wiederholtes Auftreten).
  • Aber wie ist das im Vergleich zu sort -u …?
  • @HashWizard Eine einfache sort -u ändert die Reihenfolge.Meine Antwort zeigt Lösungen, die die Reihenfolge beibehalten (genauer gesagt die Reihenfolge des ersten Auftretens).
  • @Gilles würden Sie sagen, dass es schneller ist als sort -u für große Dateien (10G) mit 50% Duplikaten ?

Antwort

sort -u big-csv-file.csv > duplicates-removed.csv 

Beachten Sie, dass die Ausgabedatei dies tut sortiert werden.

Kommentare

  • Nicht so schnell wie der Befehl awk in anderen Antworten, aber konzeptionell einfach!
  • @Johann Ich mache das ziemlich oft bei Dateien mit Hunderttausenden (sogar Millionen) von kurzen, mit Zeilenumbrüchen abgeschlossenen Zeichenfolgen. Ich bekomme die Ergebnisse für die Experimente, die ich mache, ziemlich schnell. Es kann wichtiger sein, wenn es in Skripten verwendet wird, die immer wieder ausgeführt werden. Die Zeitersparnis kann beträchtlich sein.
  • Verwenden Sie sort -u, um Duplikate während des Sortierens zu entfernen. eher als nach. (Und spart Speicherbandbreite) Weiterleiten an ein anderes Programm). Dies ist nur dann besser als die Version awk, wenn Sie auch Ihre Ausgabe sortieren möchten. (Das OP für diese Frage möchte, dass seine ursprüngliche Bestellung erhalten bleibt , daher ist dies eine gute Antwort für einen etwas anderen Anwendungsfall.)
  • Hat für mich ungefähr eine Minute gedauert eine 5,5-Millionen-Zeilendatei (insgesamt 1,8 GB). Genial.

Antwort

Angenommen, Sie können es sich leisten, so viel wie die duplizierte Datei im Speicher zu behalten ( Wenn Ihre Daten tatsächlich um den Faktor 100 dupliziert werden (dies sollte etwa 20 MB + Overhead betragen), können Sie dies mit Perl sehr einfach tun.

$ perl -ne "print unless $dup{$_}++;" input_file > output_file 

Dies behält auch die Reihenfolge bei.

Sie können die Anzahl der Vorkommen jeder Zeile aus dem %dup -Hash extrahieren, wenn Sie dies wünschen, als zusätzlichen kostenlosen Bonus.

Wenn Sie awk bevorzugen, sollte dies auch so sein (gleiche Logik wie die Perl-Version, gleiche Reihenfolge, gleiche Daten, die in der dup Variable):

$ awk "{if (++dup[$0] == 1) print $0;}" input_file > output_file 

Kommentare

  • Das ist zu gut @Mat, I. wollte gerade die Datei schlürfen, lol ;-).
  • Jetzt warte ich auf @ManAtWork für seine sed und awk magische Weberei 🙂
  • wieder super für den awk Tipp: – )
  • Ist es möglich, das Perl-Skript so zu ändern, dass es nur entfernt wird? e benachbarte Zeilen duplizieren?
  • @dumbledad: uniq macht das alles von selbst

Antwort

Da keine andere Antwort vor Ort Unterstützung bietet, ist hier eine:

gawk -i inplace "!a[$0]++" file 

Kommentare

  • Erhält dies die Reihenfolge? Das hat bei mir übrigens nicht funktioniert. Meine Version lautet: GNU Awk 4.0.2
  • @Leonid Ja, das tut es. Es wird das erste Auftreten einer eindeutigen Zeile gedruckt. Die Inplace-Unterstützung wurde erstmals in Version 4.1 eingeführt, die 2013 veröffentlicht wurde.
  • Dies sollte die Antwort sein. Es ‚ löscht tatsächlich die duplizierte Zeichenfolge in der vorhandenen oder aktuellen Datei, wobei die oberste Antwort und die meisten Antworten hier nur die eindeutigen / duplizierten Zeichenfolgen ausdrucken und nichts tun, und wir müssen erstellen eine weitere Ausgabe zum Speichern des Ergebnisses.

Antwort

Sie können uniq http://www.computerhope.com/unix/uuniq.htm

uniq meldet oder filtert wiederholte Zeilen in einer Datei heraus.

Kommentare

  • Wenn Sie eine Antwort geben, ist es vorzuziehen, eine Erklärung, warum Ihre Antwort die ist. Wie unterscheidet sich diese Antwort von mehreren der vorherigen Antworten?
  • Auf der Uniq-Manpage: Hinweis: 'uniq' does not detect repeated lines unless they are adjacent. Sie müssen sie also zuerst sortieren und verlieren die Reihenfolge der nicht doppelten Zeilen.

Antwort

Python One-Liner:

python -c "import sys; lines = sys.stdin.readlines(); print "".join(sorted(set(lines)))" < InputFile 

Kommentare

    ies führt dazu, dass die gesamte Datei in den Speicher verschoben wird und möglicherweise nicht für das Problem des OP ‚ geeignet ist. Auch nicht garantiert, um Ordnung zu behalten

  • Vielen Dank für den Vorschlag, ich ‚ habe gerade Python gelernt .. habe dies nur zu Lernzwecken versucht .. 🙂
  • Hier ‚ s eine Python 2.7-Version, die kein Einzeiler ist, aber (kurz) Gibt eindeutige Zeilen zurück, die die Reihenfolge beibehalten, ohne die gesamte Datei in den Speicher zu laden oder eine einzelne gigantische Zeichenfolge zum Drucken zu erstellen.
  • Danke @ 1_CR Ich habe heute etwas gelernt 🙂 OrderedDict

Antwort

Keine der Antworten hier funktionierte für mich auf meinem Mac, daher habe ich eine einfache Python geschrieben Skript, das für mich funktioniert. Ich ignoriere führende / nachfolgende Leerzeichen und kümmere mich auch nicht um den Speicherverbrauch.

import sys inputfile = sys.argv[1] outputfile = sys.argv[2] with open(inputfile) as f: content = f.readlines() content = [x.strip() for x in content] my_list = list(set(content)) with open(outputfile, "w") as output: for item in my_list: output.write("%s\n" % item) 

Speichern Sie das oben Gesagte als eindeutig.py und laufen Sie wie folgt ab:

python unique.py inputfile.txt outputfile.txt 

Antwort

LÖSUNG OHNE WARTUNG DER ORIGINALFOLGENBESTELLUNG

Ich habe es mit dem folgenden Codeteil gemacht.

sort duplicates.txt | uniq > noDuplicates.txt 

Der Befehl sort sortiert die Zeilen alphabetisch und der Befehl uniq entfernt die Duplikate.

HINWEIS: Warum wir die Zeilen zuerst sortiert haben, ist, dass uniq erkennt keine doppelten Zeilen, es sei denn, sie sind benachbart.

Kommentare

  • In der Frage wird nach einer Methode gefragt (vorzugsweise) ) die die Eingabereihenfolge beibehält; Könnten Sie Ihre Antwort bearbeiten , um dies zu beheben? Beachten Sie, dass bereits Antworten mit sort vorhanden sind, die die Eingabereihenfolge beibehalten, und eine Antwort mit sort ohne Beibehaltung der Eingabereihenfolge, jedoch effizienter als das Weiterleiten an uniq.
  • @StephenKitt Bearbeitet. Ich habe andere Antworten überprüft, konnte aber ‚ nur mit grundlegenden Befehlen nichts finden. Vielen Dank für Ihr Feedback.
  • Ich habe Ihnen einen Link zu einer Antwort mit nur grundlegenden Befehlen gegeben, tatsächlich nur einem Befehl, sort -u (das ist Teil von POSIX ) ;-).
  • @StephenKitt Ich habe diese Antwort gesehen. Meins ist auch eine Möglichkeit, das Problem zu lösen. Was soll ich mehr tun? Soll ich die Antwort löschen?
  • Nein, löschen Sie Ihre Antwort nicht. Ich wollte nur sicherstellen, dass Sie sich der anderen Antwort bewusst sind, da Sie sagten, Sie könnten „‚ nichts nur mit grundlegenden Befehlen finden“.

Antwort

Mit bash 4 eine reine Bash-Lösung, die die Vorteile von assoziativen Arrays nutzt kann verwendet werden. Hier ist ein Beispiel

unset llist; declare -A llist; while read -r line; do if [[ ${llist[$line]} ]]; then continue else printf "%s\n" "$line" llist[$line]="x" fi done < file.txt 

Kommentare

  • Don ‚ Verwenden Sie keine read -Schleifen, um große Textdateien zu verarbeiten. bash muss jeweils ein Byte lesen, um ein Überschießen einer neuen Zeile zu vermeiden. Bash ist auch bei der Textverarbeitung im Allgemeinen im Vergleich zu awk nicht sehr schnell. Wenn Sie dies verwenden, vermeidet read -ra das Essen von Backslashes in Ihrer Eingabe. Vergessen Sie auch nicht, ‚ unset llist nach der Schleife zu verwenden, wenn Sie dies in eine Shell-Funktion oder einfügen Verwenden Sie es interaktiv.
  • @PeterCordes, oder Sie hätten einfach auf this verweisen können 🙂

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.