Wie entferne ich alle Zeilen in einer Datei, die weniger als 6 Zeichen enthalten?

17

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?

Sag mir warum
quelle
Ist diese Frage nicht besser für Stackoverflow geeignet?
user1073075
2
@ user1073075 hier ist es perfekt zum thema.
Seth

Antworten:

30

Dafür gibt es viele Möglichkeiten.

Verwenden von grep:

grep -E '^.{6,}$' file.txt >out.txt

Jetzt out.txtenthä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" grepspeichern oder die Datei direkt bearbeiten, indem Sie folgende -iOptionen verwenden sed:

sed -ri.bak '/^.{6,}$/' file.txt 

Die Originaldatei wird wie folgt gesichert: file.txt.bakDie 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')
heemayl
quelle
Yay! Ich hatte auf eine Python-Antwort gehofft =)
TellMeWhy
@ DevRobot Ich sehe .. Wenn Check-out-Liste Verständnis, das ich hinzugefügt habe, mehr Pythonic sein ..
Heemayl
1
Auch @DevRobot ist sich nicht so sicher, ob Python bei großen Dateien langsamer ist, wenn die erste Option verwendet wird. Eigentlich bin ich mir ziemlich sicher, dass Python auf Millionen von Zeilen schneller ist, da es pro Zeile gelesen wird.
Jacob Vlijm
1
Das zweite Python-Beispiel liest die gesamte Datei in den Speicher, bevor der Join ausgeführt wird. Ich denke, das erste Python-Beispiel ist in diesem Fall besser.
Holloway
Das Lesen von Zeilen ist notwendigerweise langsamer, da Dateien nicht so strukturiert sind. Sie müssen ohnehin einen Block vorauslesen und nach einer Newline mit reduzierten Parallelisierungsmöglichkeiten suchen, und dann nur den Teilstring zurückgeben. Sie benötigen einen Umlaufpuffer. Sie müssen Speicher dynamisch zuweisen, wenn Sie nicht wissen, wie lang die Zeilen sein können.
The Vee
19

Es ist sehr einfach:

grep ...... inputfile > resultfile   #There are 6 dots

Dies ist äußerst effizient, da grepnicht 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.

Olivier Dulac
quelle
14

Lösung 1: Verwenden von C

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 1000000Zeichen pro Zeile begrenzt; Sie können dies ändern, indem Sie den Wert von ändern MAX_BUFFER_SIZE).

(Trick zum Ersetzen \nmit 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

Lösung 2: Verwenden von AWK:

awk 'length>=6' file
  • length>=6: length>=6Gibt TRUE zurück, wird der aktuelle Datensatz gedruckt.

Lösung 3: Verwenden von Perl:

perl -lne 'length>=6&&print' file
  • Wenn lenght>=6TRUE 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
kos
quelle
1
Glauben Sie mir ... Ich habe auf Ihre awk Lösung gewartet ...
heemayl
2
@heemayl Und ich habe die Frage nicht sofort gesehen, also wusste ich , dass du schneller gewesen wärst, wenn du zufällig online gewesen wärst. Musste meine sedLösung löschen (es passiert, ich weiß). XD
kos
Was ist der Sinn der posVariablen? Ich bekomme es einen Zeiger auf das Zeichen in linemit einem Newline-Zeichen zurück, aber Sie scheinen es nie zu verwenden. Und wenn Sie es nicht finden, setzen Sie es einfach gleich \0.
user1717828
@ user1717828 Wenn ich es finde , ersetze ich es durch \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, \0damit 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.
Kos
1
@tripleee Die Idee war, eine Lösung hinzuzufügen, die für mehr als einen einmaligen Auftrag oder für noch größere Dateien nützlich ist, aber : Ich habe die grepLö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.
Kos
2

Sie können Vim im Ex-Modus verwenden:

ex -sc 'v/\v.{6}/d' -cx file
  1. \v schalte die Magie ein

  2. .{6} Finde Zeilen mit 6 oder mehr Zeichen

  3. v Auswahl umkehren

  4. d löschen

  5. x speichern und schließen

Steven Penny
quelle
1

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

Sergiy Kolodyazhnyy
quelle