Ich habe eine Datei mit ungefähr 10 Millionen Zeilen.
Ich möchte alle Zeilen in der Datei entfernen, die weniger als sechs Zeichen enthalten.
Wie mache ich das?
Ich habe eine Datei mit ungefähr 10 Millionen Zeilen.
Ich möchte alle Zeilen in der Datei entfernen, die weniger als sechs Zeichen enthalten.
Wie mache ich das?
Antworten:
Dafür gibt es viele Möglichkeiten.
Verwenden von grep
:
grep -E '^.{6,}$' file.txt >out.txt
Jetzt out.txt
enthält Zeilen mit sechs oder mehr Zeichen.
Umgekehrter Weg:
grep -vE '^.{,5}$' file.txt >out.txt
Verwenden sed
, Entfernen von Zeilen mit einer Länge von 5 oder weniger:
sed -r '/^.{,5}$/d' file.txt
In umgekehrter Reihenfolge werden Zeilen mit einer Länge von sechs oder mehr gedruckt:
sed -nr '/^.{6,}$/p' file.txt
Sie können die Ausgabe in einer anderen Datei mit dem >
Operator "like" grep
speichern oder die Datei direkt bearbeiten, indem Sie folgende -i
Optionen verwenden sed
:
sed -ri.bak '/^.{6,}$/' file.txt
Die Originaldatei wird wie folgt gesichert: file.txt.bak
Die geänderte Datei wird gesichert file.txt
.
Wenn Sie keine Sicherungskopie erstellen möchten:
sed -ri '/^.{6,}$/' file.txt
Verwenden Sie die Shell, Langsamer, Tun Sie dies nicht , um eine andere Methode zu zeigen:
while IFS= read -r line; do [ "${#line}" -ge 6 ] && echo "$line"; done <file.txt
Unter Verwendung python
, sogar langsamer als grep
, sed
:
#!/usr/bin/env python2
with open('file.txt') as f:
for line in f:
if len(line.rstrip('\n')) >= 6:
print line.rstrip('\n')
Verwenden Sie das Listenverständnis besser, um pythonischer zu sein:
#!/usr/bin/env python2
with open('file.txt') as f:
strip = str.rstrip
print '\n'.join([line for line in f if len(strip(line, '\n')) >= 6]).rstrip('\n')
Es ist sehr einfach:
grep ...... inputfile > resultfile #There are 6 dots
Dies ist äußerst effizient, da grep
nicht versucht wird, mehr als erforderlich zu analysieren oder die Zeichen in irgendeiner Weise zu interpretieren: Es wird einfach eine (vollständige) Zeile an stdout gesendet (die die Shell dann an resultfile weiterleitet) , sobald 6 angezeigt wird Zeichen in dieser Zeile (entspricht .
in einem regulären Ausdruck einem beliebigen Zeichen).
Grep gibt also nur Zeilen mit 6 (oder mehr) Zeichen aus, und die anderen werden nicht von grep ausgegeben, damit sie nicht in die Ergebnisdatei gelangen.
Schnellster Weg: Kompilieren und starten Sie dieses C-Programm:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_BUFFER_SIZE 1000000
int main(int argc, char *argv[]) {
int length;
if(argc == 3)
length = atoi(argv[2]);
else
return 1;
FILE *file = fopen(argv[1], "r");
if(file != NULL) {
char line[MAX_BUFFER_SIZE];
while(fgets(line, sizeof line, file) != NULL) {
char *pos;
if((pos = strchr(line, '\n')) != NULL)
*pos = '\0';
if(strlen(line) >= length)
printf("%s\n", line);
}
fclose(file);
}
else {
perror(argv[1]);
return 1;
}
return 0;
}
Kompilieren Sie mit gcc program.c -o program
, führen Sie mit aus ./program file line_length
(wobei file
= Pfad zur Datei und line_length
= minimale Zeilenlänge in Ihrem Fall 6
; die maximale Zeilenlänge ist auf 1000000
Zeichen pro Zeile begrenzt; Sie können dies ändern, indem Sie den Wert von ändern MAX_BUFFER_SIZE
).
(Trick zum Ersetzen \n
mit hier\0
gefunden .)
Vergleich mit allen anderen Lösungen, die für diese Frage vorgeschlagen wurden, mit Ausnahme der Shell-Lösung (Testlauf mit einer ~ 91-MB-Datei mit 10-MB-Zeilen und einer durchschnittlichen Länge von 8 Zeichen):
time ./foo file 6
real 0m1.592s
user 0m0.712s
sys 0m0.160s
time grep ...... file
real 0m1.945s
user 0m0.912s
sys 0m0.176s
time grep -E '^.{6,}$'
real 0m2.178s
user 0m1.124s
sys 0m0.152s
time awk 'length>=6' file
real 0m2.261s
user 0m1.228s
sys 0m0.160s
time perl -lne 'length>=6&&print' file
real 0m4.252s
user 0m3.220s
sys 0m0.164s
sed -r '/^.{,5}$/d' file >out
real 0m7.947s
user 0m7.064s
sys 0m0.120s
./script.py >out
real 0m8.154s
user 0m7.184s
sys 0m0.164s
awk 'length>=6' file
length>=6
: length>=6
Gibt TRUE zurück, wird der aktuelle Datensatz gedruckt.perl -lne 'length>=6&&print' file
lenght>=6
TRUE zurückgegeben wird, wird der aktuelle Datensatz gedruckt.% cat file
a
bb
ccc
dddd
eeeee
ffffff
ggggggg
% ./foo file 6
ffffff
ggggggg
% awk 'length>=6' file
ffffff
ggggggg
% perl -lne 'length>=6&&print' file
ffffff
ggggggg
awk
Lösung gewartet ...
sed
Lösung löschen (es passiert, ich weiß). XD
pos
Variablen? Ich bekomme es einen Zeiger auf das Zeichen in line
mit einem Newline-Zeichen zurück, aber Sie scheinen es nie zu verwenden. Und wenn Sie es nicht finden, setzen Sie es einfach gleich \0
.
\0
( strchr()
gibt einen NULL-Zeiger zurück, wenn das Zeichen nicht gefunden wird). Es geht darum, jede neue Zeile am Ende jeder Zeile durch eine neue zu ersetzen, \0
damit die neue Zeile niemals gezählt wird strlen()
. Dies bedeutet, dass die Länge immer mit 6 verglichen werden kann, unabhängig davon, ob in der letzten Zeile möglicherweise eine neue Zeile fehlt. Ich weiß, dass es viel effizienter ist, nur die letzte Zeile anders zu behandeln. Ich werde das wahrscheinlich später aktualisieren.
grep
Lösung für dieselbe Datei getestet und sie ist tatsächlich schneller (wahrscheinlich, weil dies strlen()
hier nicht die beste Idee ist). . Ich werde versuchen, eine getchar()
Schleife zu verwenden, um stattdessen nur das erste N-Zeichen zu überprüfen. Ich denke, das sollte es sichtbar verbessern. Und ja, jede Linie über die Länge des Puffers wird einfach auf die Länge des Puffers zugeschnitten.
Sie können Vim im Ex-Modus verwenden:
ex -sc 'v/\v.{6}/d' -cx file
\v
schalte die Magie ein
.{6}
Finde Zeilen mit 6 oder mehr Zeichen
v
Auswahl umkehren
d
löschen
x
speichern und schließen
Ruby-Lösung:
$ cat input.txt
abcdef
abc
abcdefghijk
$ ruby -ne 'puts $_ if $_.chomp.length() >= 6 ' < input.txt
abcdef
abcdefghijk
Einfache Idee: Leiten Sie die Datei in Rubys Standard um und drucken Sie die Zeile nur dann aus dem Standard, wenn die Länge größer oder gleich 6 ist