Ich habe zwei Textdateien. Eine ist eine Textdatei mit Name, E-Mail-Adresse und anderen Feldern. Einige Zeilen aus file1
:
John:[email protected]:johnson123:22hey
Erik:[email protected]:johnson133:22hey
Robert:[email protected]:johnson123:21hey
Johnnny:[email protected]:johnson123:22hey
Die andere enthält nur E-Mail-Adressen. Beispiele aus file2
:
[email protected]
[email protected]
[email protected]
[email protected]
Ich möchte, dass die Ausgabe in jeder vollständigen Zeile file1
eine E-Mail-Adresse enthält file2
. Ist zum Beispiel [email protected]
in file2
, also würde ich gerne die folgende Zeile von sehen file1
:
John:[email protected]:johnson123:22hey
Gibt es eine einfache Möglichkeit, file1
die Zeilen zu suchen und auszugeben, die mit der "Liste der E-Mail-Adressen" übereinstimmen file2
?
Ich habe nach STUNDEN gesucht, aber meine Google-Suche (und StackOverflow-Suche) sowie die Bemühungen in der Befehlszeile waren bisher nicht effektiv.
Befehle, die ich ausprobiert habe und von denen ich denke, dass sie funktionieren würden:
fgrep -f file2.txt file1.txt > matched.txt
grep -F -f ....
grep -F -x -f file1 file2 > common
usw., aber sie haben alle grep memory exhausted
- die Dateien, mit denen ich übereinstimme, sind 4,8 GB ( file1
) und 3,2 GB ( file2
die nur die E-Mail-Adressen enthalten). Ich gehe davon aus, dass der Speicher mit diesen Befehlen erschöpft ist. Ich habe eine Methode gefunden, mit find
der die Befehle reibungsloser ausgeführt werden können, aber sie hat nicht funktioniert.
tldr ; Notwendigkeit übereinstimmen file2
mit file1
und wenn es eine Zeile aus , file2
dass entspricht einer Zeile in die file1
Ausgangs es. Die Dateien sind groß und ich brauche einen sicheren Weg, um nicht den gesamten Speicher zu verbrauchen.
Danke, habe den ganzen Tag danach gesucht und experimentiert, wollte nicht aufgeben (5 Stunden +).
quelle
Antworten:
Es ist ziemlich schwierig, große Dateien zu bedienen, aber Sie können dies in drei Schritten tun:
Sortieren Sie Datei1 nach dem zweiten Feld
Sortieren file2
Verbinden Sie 2 Dateien per E-Mail-Feld
quelle
:
was im lokalen Teil einer E-Mail-Adresse auftreten kann.Ich sende eine zweite Antwort auf diese Frage (dies ist ein interessantes Problem). Diese Lösung unterscheidet sich grundlegend von meiner SQLite-Lösung und von den vielversprechend aussehenden
sort
+join
Lösungen, die sich abzeichnen:Verwenden Sie Ihren anfänglichen Ansatz mit
grep -f
, aber reduzieren Sie das Problem buchstäblich ein wenig. Teilen wir die "Abfragedatei"file2
mithilfe von in verwaltbare Blöcke aufsplit
.Das
split
Dienstprogramm kann eine Datei basierend auf der Zeilenanzahl in mehrere kleinere Dateien aufteilen.Eine 3,2-GB-Datei mit einer durchschnittlichen Zeilenlänge von 20 Zeichen enthält ungefähr 172.000.000 Zeilen (es sei denn, ich habe einen Rechenfehler gemacht). Das Aufteilen in 2000 Dateien mit 85000 Zeilen pro Datei ist möglich.
Damit,
Die
-a 4
Option weistsplit
an, nach einer Initiale vier Zeichenx
zu verwenden, um die Dateinamen für die neuen Dateien zu erstellen. Die Dateien werden aufgerufen werdenxaaaa
,xaaab
usw.Führen Sie dann das Original
grep -f
auf diesen aus:Dies kann es ermöglichen
grep
, den jetzt viel kleineren Satz von Abfragemustern im Speicher zu halten.UPDATE : Mit 145.526.885 Zeilen können Sie
split -l 72000 -a 4
ungefähr 2000 Dateien erstellen.Denken Sie daran, das
testing
Verzeichnis jedes Mal zu löschen, wenn Sie versuchen, einen neuen Satz geteilter Dateien zu erstellen.Beachten Sie, dass die geteilten Dateien aus dieser Antwort einzeln als Eingabe für alle anderen Antworten verwendet werden können, die Sie möglicherweise auf diese Frage erhalten.
quelle
Die Antwort von Costas ist angesichts Ihres genauen Problems wahrscheinlich die beste, da Sie ein Feld haben, das zu 100% übereinstimmt.
Aber wenn Ihr Problem wirklich war für Millionen von regexps in Milliarden von Zeilen greppen, dann hat GNU Parallel eine Beschreibung, wie das zu tun: https://www.gnu.org/software/parallel/man.html#EXAMPLE:-Grepping -n-Zeilen-für-m-reguläre-Ausdrücke
Die einfachste Lösung, um eine große Datei für viele reguläre Ausdrücke zu durchsuchen, ist:
Oder wenn die regulären Ausdrücke feste Zeichenfolgen sind:
Es gibt drei einschränkende Faktoren: CPU, RAM und Festplatten-E / A.
RAM ist einfach zu messen: Wenn der grep-Prozess den größten Teil Ihres freien Speichers beansprucht (z. B. wenn Sie oben ausgeführt werden), ist RAM ein begrenzender Faktor.
Die CPU ist auch leicht zu messen: Wenn der grep> 90% der CPU oben einnimmt, ist die CPU ein begrenzender Faktor, und die Parallelisierung beschleunigt dies.
Es ist schwieriger zu erkennen, ob die Festplatten-E / A der begrenzende Faktor ist, und je nach Festplattensystem kann die Parallelisierung schneller oder langsamer sein. Der einzige Weg, um sicher zu wissen, ist zu testen und zu messen.
Begrenzungsfaktor: RAM
Die normale grep -f regexs.txt-Bigfile funktioniert unabhängig von der Größe der Bigfile. Wenn die regexps.txt jedoch so groß ist, dass sie nicht in den Speicher passt, müssen Sie sie aufteilen.
grep -F benötigt ungefähr 100 Bytes RAM und grep ungefähr 500 Bytes RAM pro 1 Byte Regexp. Wenn also regexps.txt 1% Ihres Arbeitsspeichers ausmacht, ist es möglicherweise zu groß.
Wenn Sie Ihre regulären Ausdrücke in feste Zeichenfolgen konvertieren können, tun Sie dies. ZB wenn die Zeilen, nach denen Sie in bigfile suchen, alle so aussehen:
dann kann Ihre regexps.txt konvertiert werden von:
in:
Auf diese Weise können Sie grep -F verwenden, das etwa 80% weniger Speicher benötigt und viel schneller ist.
Wenn es immer noch nicht in den Speicher passt, können Sie dies tun:
Die 1M sollte Ihr freier Speicher sein, geteilt durch die Anzahl der Kerne und geteilt durch 200 für grep -F und durch 1000 für normales grep. Unter GNU / Linux können Sie Folgendes tun:
Wenn Sie mit doppelten Zeilen und falscher Reihenfolge leben können, ist dies schneller:
Begrenzungsfaktor: CPU
Wenn die CPU der begrenzende Faktor ist, sollte die Parallelisierung auf den regulären Ausdrücken erfolgen:
Der Befehl startet einen Grep pro CPU und liest Bigfile einmal pro CPU. Da dies jedoch parallel erfolgt, werden alle Lesevorgänge mit Ausnahme des ersten im RAM zwischengespeichert. Abhängig von der Größe von regexp.txt ist es möglicherweise schneller, --block 10m anstelle von -L1000 zu verwenden.
Einige Speichersysteme bieten eine bessere Leistung, wenn mehrere Blöcke gleichzeitig gelesen werden. Dies gilt für einige RAID-Systeme und für einige Netzwerkdateisysteme. Um das Lesen von Bigfile zu parallelisieren:
Dadurch wird bigfile in 100-MB-Blöcke aufgeteilt und grep für jeden dieser Blöcke ausgeführt. Um sowohl das Lesen von bigfile als auch von regexp.txt zu parallelisieren, kombinieren Sie beide mit --fifo:
Wenn eine Zeile mit mehreren regulären Ausdrücken übereinstimmt, wird die Zeile möglicherweise dupliziert.
Größeres Problem
Wenn das Problem zu groß ist, um damit gelöst zu werden, sind Sie wahrscheinlich bereit für Lucene.
quelle
Wichtiger Haftungsausschluss: Ich habe dies anhand der in der Frage angegebenen Daten getestet. Das Laden mehrerer Gigabyte Daten in eine SQLite-Datenbank kann viel Zeit in Anspruch nehmen. Die Abfrage unter Verwendung von zwei Textfeldern kann ineffizient sein. Die Festplattenleistung kann berücksichtigt werden. Usw. usw.
Das folgende
sh
Skript erstellt die SQLlite-Datenbankdatabase.db
(diese Datei wird gelöscht, wenn sie bereits vorhanden ist), erstellt die Tabellenqadr
unddata
und lädt die Daten in die beiden Tabellen (file1
indata
undfile2
inqadr
). Anschließend wird ein Index für erstelltdata.adr
.Bei der Erstellung des Index wird davon ausgegangen, dass die Adressen in
file1
eindeutig sind (:
dh , dass das Feld mit der zweiten Begrenzung eindeutig ist). Wenn dies nichtUNIQUE
derCREATE INDEX
Fall ist, entfernen Sie es aus der Anweisung (im Idealfall sind sie eindeutig, und im Idealfallfile2
sind auch die Zeilen in eindeutig).Ich habe noch nie mit SQLite und diesen Datenmengen gearbeitet, aber ich weiß, dass Multi-Gigabyte-Importe in MongoDB und MySQL schmerzhaft langsam sein können und dass die Indexerstellung ebenfalls zeitaufwändig sein kann. Ich sage also im Grunde, dass ich das nur für jemanden mit vielen Daten zum Testen rauswerfe.
Dann handelt es sich um eine einfache Abfrage:
oder vielleicht sogar nur
Jemand mit mehr SQLite-Kenntnissen wird dies sicherlich konstruktiv kommentieren.
quelle
:
als Trenner zu verwenden, ist zu simpel. A:
kann sich im lokalen Teil einer gültigen E-Mail-Adresse befinden.:
Separator kann einfach mit awk oder perl befestigt werden. Aufteilen in ein Array mit: als Trennzeichen. Wenn das Array 4 Felder enthält, verwenden Sie es unverändert. Wenn es 5 Felder hat, verbinden Sie die Felder 2 und 3 mit einem :, löschen Sie Feld 3 und verwenden Sie dann. "use" kann so einfach sein wie die Ausgabe mit TAB-Trennzeichen und Pipe in SQLite für den Import. oder richtig zitiert und CSV. oder json oder XML. Übrigens würde ich bei Dateien dieser Größe dazu neigen, postgresql oder mysql anstelle von sqlite zu verwenden.Wenn Sie eine DB-Lösung vermeiden müssen (nicht sicher, warum, scheint mir die beste Idee zu sein), können Sie dies tun, indem Sie die beiden Dateien nach den E-Mail-Adressen sortieren und dann den
join
Befehl verwenden, der ungefähr der Leistung einer DB entspricht.Folgendes habe ich getan:
Das scheint mit Ihren Beispieldaten das Richtige zu tun. Es sortiert die Dateien an Ort und Stelle . Wenn Sie das nicht möchten, ändern Sie die
-o
Option auf demsort
s in temporäre Dateinamen und verwenden Sie diese im Join. Wenn Sie tatsächlich andere als 4 Felder in der ersten Datei haben, müssen Sie dies in der-o
Option berücksichtigenjoin
.Weitere Informationen finden Sie in den Manpages.
quelle
So etwas würde funktionieren, aber ich bin mir nicht sicher, ob es eine gute Idee ist, abhängig von Ihrem Anwendungsfall (ungetestet):
Eine weitere mögliche Lösung, wenn Sie mehr von einer Einzeiler-Methode möchten (unten schnell getestet):
Was ergab:
quelle
Hier ist eine Version von Kusalanandas Skript, mit
perl
der vor dem Einspeisenfile1
von:
getrennt in getrenntes TAB umgewandelt wirdsqlite3
.Das eingebettete
perl
Skript prüft, ob 5 statt 4 Felder vorhanden sind. Wenn dies der Fall ist, hängt es Feld 3 an Feld 2 an (wobei das:
vom Autosplit entfernte Feld wiederhergestellt wird ) und löscht dann Feld 3.IMO, SQLite ist nicht für eine so große Datenbank geeignet. Ich würde empfehlen, stattdessen
mysql
oder zupostgresql
verwenden. Für diese Art von Aufgabemysql
ist es aufgrund der Geschwindigkeit wahrscheinlich eine bessere Wahl - für einfache Dinge wie diese ist es schneller, aber für komplexere Aufgaben ist postgresql viel schneller - meiner Erfahrung nach ist pg "smart fast" (dh es kann massiv erreichen Geschwindigkeitsverbesserungen bei komplexen Aufgaben durch intelligentes Arbeiten anstatt hartes Arbeiten), MySQL ist "dumm schnell" (dh es arbeitet hart, ohne viel Fähigkeit, intelligent zu arbeiten).Das obige Skript kann leicht angepasst werden, um mit den
psql
odermysql
Befehlszeilen-Clients zu arbeitensqlite3
, aber ich würde dieCREATE TABLE
Befehle so ändern , dassCHARACTER(size)
stattdessen eine feste Größe verwendet wirdTEXT
, wobeisize
eine vernünftige Vermutung vorliegt, wie groß die maximale Größe für jedes Feld ist - z. B. vielleicht 255 Zeichen für dasadr
Feld und 10-50 Zeichen für die anderen.Eine mögliche Optimierung besteht darin, die Feldgrößen sorgfältig so auszuwählen, dass jeder Datensatz ein gleichmäßiger Teiler der Blockgröße Ihres Laufwerks ist (unter Berücksichtigung des Overheads von mysql / postgresql pro Datensatz). 512 Bytes sollten für alle gängigen Blockgrößen geeignet sein. Machen Sie die Felder in der gewünschten Größe und fügen Sie ein zusätzliches, nicht verwendetes
CHARACTER(size)
Feld hinzu, um den Unterschied auszugleichen. Der Grund dafür ist, dass Datensätze niemals eine Blockgrenze überschreiten, sodass die Datenbank-Engine immer nur einen Plattenblock einlesen muss, um alle Daten für einen bestimmten Datensatz abzurufen (tatsächlich werden mehrere Datensätze in einem Block mit gelesen Die meisten aktuellen Blockgrößen, aber das hilft nur der Leistung, kann sie nicht beeinträchtigen.https://dba.stackexchange.com/ ist wahrscheinlich die beste Website, um nach Informationen zur Optimierung der Datensatzgröße zu suchen oder diese anzufordern.
quelle