2017-01-03 11 views
1

Ich suche bestimmte Gruppen von Zeilen aus großen (~ 870.000.000 Zeilen) Textdateien. Zum Beispiel könnte ich in einer Datei mit 50 Zeilen die Zeilen 3-6, 18-27 und 39-45 wünschen.Zeilen aus einer großen Textdatei lesen

Von Browsing Stack-Überlauf, ich habe, dass der Bash-Befehl gefunden:

tail -n+NUMstart file |head -nNUMend 

der schnellste Weg ist eine einzige Zeile oder eine Gruppe von Linien bei NUMSTART beginnen zu bekommen und zu NUMend gehen. Beim Lesen mehrerer Liniengruppen scheint dies jedoch ineffizient zu sein. Normalerweise ist die Technik nicht so wichtig, aber mit so großen Dateien macht es einen großen Unterschied.

Gibt es einen besseren Weg, als den obigen Befehl für jede Gruppe von Linien zu verwenden? Ich gehe davon aus, dass die Antwort höchstwahrscheinlich ein Bash-Befehl sein wird, aber ich bin wirklich offen für jede Sprache/jedes Werkzeug, das den Job am besten erfüllt.

Antwort

3

Linien Um zu zeigen, 3-6, 18-27 und 39-45 mit sed:

sed -n "3,6p;18,27p;39,45p" file 

Es ist auch möglich, von Futtermittel sed eine Datei.

Inhalt der Datei foobar:

 
3,6p 
18,27p 
39,45p 

Verbrauch:

sed -n -f foobar file 
+0

Ich bin gespannt, ob das viel schneller wäre als 'awk'. – codeforester

+1

Dies wird die gesamte Datei scannen, kann nicht schneller sein als 'awk' mit einem Ausgang nach der letzten Zeile. – karakfa

+2

Das Hinzufügen von '45q' als letzten Befehl würde das beheben. –

0

Das Problem mit tail -n XX file | head -n YY für verschiedene Bereiche ist, dass Sie es mehrmals ausführen, daher die Ineffizienz. Ansonsten empfehlen benchmarks, dass sie die beste Lösung sind.

awk '(NR>=start1 && NR<=end1) || (NR>=start2 && NR<=end2) || ...' file 

In Ihrem Fall:

Für diesen speziellen Fall können Sie awk verwenden möchten

awk '(NR>=3 && NR<=6) || (NR>=18 && NR<=27) || (NR>=39 && NR<=45)' file 

Das heißt, Sie Gruppe die Bereiche und lassen awk Druck die entsprechenden Leitungen, wenn sie auftreten, nur einmal durch die Datei durchlaufen. Es kann auch nützlich sein, eine letzte NR==endX {exit} (endX ist die schließende Position aus dem letzten Bereich) hinzuzufügen, so dass es die Verarbeitung beendet, sobald es die letzte interessante Zeile gelesen hat.

In Ihrem Fall:

awk '(NR>=3 && NR<=6) || (NR>=18 && NR<=27) || (NR>=39 && NR<=45); NR==45 {exit}' file 
1

awk zur Rettung!

awk -v lines='3-6,18-27,39-45' ' 
     BEGIN {n=split(lines,a,","); 
       for(i=1;i<=n;i++) 
       {split(a[i],t,"-"); 
       rs[++c]=t[1]; re[c]=t[2]}} 

      {for(i=s;i<=c;i++) 
       if(NR>=rs[i] && NR<=re[i]) {print; next} 
       else if(NR>re[i]) s++; 
       if(s>c) exit}' file 

bietet einen frühen Ausgang nach der letzten gedruckten Zeile. Keine Fehlerprüfung, die Bereiche sollten in aufsteigender Reihenfolge angegeben werden.

+0

Guter Ansatz. Ich glaube jedoch nicht, dass die Reichweitenfütterung hier ein besonderes Problem darstellt, daher ist der BEGIN-Block irgendwie irrelevant (für mich). – fedorqui

Verwandte Themen