Mam plik zawierający około 10 milionów linii.
Chcę usunąć wszystkie wiersze w pliku, które mają mniej niż sześć znaków.
Jak mam to zrobic?
Mam plik zawierający około 10 milionów linii.
Chcę usunąć wszystkie wiersze w pliku, które mają mniej niż sześć znaków.
Jak mam to zrobic?
Odpowiedzi:
Istnieje wiele sposobów, aby to zrobić.
Używanie grep:
grep -E '^.{6,}$' file.txt >out.txt
Teraz out.txtbędzie zawierać wiersze mające sześć lub więcej znaków.
Odwrotna droga:
grep -vE '^.{,5}$' file.txt >out.txt
Używanie sed, usuwanie linii o długości 5 lub mniejszej:
sed -r '/^.{,5}$/d' file.txt
Odwrotnie, drukowanie linii o długości sześciu lub większej:
sed -nr '/^.{6,}$/p' file.txt
Możesz zapisać dane wyjściowe w innym pliku za pomocą >operatora podobnego greplub edytować plik w miejscu za pomocą -iopcji sed:
sed -ri.bak '/^.{6,}$/' file.txt
Kopia zapasowa oryginalnego pliku zostanie utworzona, file.txt.baka zmodyfikowany plik będzie file.txt.
Jeśli nie chcesz przechowywać kopii zapasowej:
sed -ri '/^.{6,}$/' file.txt
Używając powłoki, Wolniej, nie rób tego , to tylko w celu pokazania innej metody:
while IFS= read -r line; do [ "${#line}" -ge 6 ] && echo "$line"; done <file.txt
Korzystanie pythonnawet wolniej niż 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')
Lepsze wykorzystanie rozumienia listy, aby być bardziej Pythonicznym:
#!/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')
To jest bardzo proste:
grep ...... inputfile > resultfile #There are 6 dots
Jest to niezwykle wydajne, ponieważ grepnie będzie parsowało więcej niż potrzebuje, ani nie interpretuje znaków w żaden sposób: po prostu wysyła (całą) linię do standardowego wyjścia (które powłoka przekierowuje do pliku wynikowego), gdy tylko zobaczy 6 znaki w tej linii ( .w kontekście wyrażenia regularnego dopasowuje dowolny 1 znak).
Zatem grep wypisuje tylko wiersze zawierające 6 (lub więcej) znaków, a pozostałe nie są wyprowadzane przez grep, więc nie robią tego z plikiem wynikowym.
Najszybszy sposób: skompiluj i uruchom ten program C:
#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;
}
Skompiluj z gcc program.c -o program, uruchom z ./program file line_length(gdzie file= ścieżka do pliku i line_length= minimalna długość linii, w twoim przypadku 6; maksymalna długość linii jest ograniczona do 1000000znaków na linię; możesz to zmienić, zmieniając wartość MAX_BUFFER_SIZE).
(Trick zastąpił \nze \0znalezionych tutaj ).
Porównanie ze wszystkimi innymi rozwiązaniami zaproponowanymi w tym pytaniu, z wyjątkiem rozwiązania powłoki (uruchomienie testowe na pliku ~ 91 MB z liniami 10M o średniej długości 8 znaków):
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: jeśli length>=6zwraca PRAWDA, drukuje bieżący rekord.perl -lne 'length>=6&&print' file
lenght>=6zwróci wartość PRAWDA, drukuje bieżący rekord.% 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 rozwiązanie ..
sedrozwiązanie (zdarza się, wiem). XD
poszmiennej? Rozumiem, że zwraca wskaźnik do znaku lineze znakiem nowej linii, ale wydaje się, że nigdy go nie używasz. A jeśli go nie znajdziesz, po prostu ustaw go na równy \0.
\0( strchr()zwraca wskaźnik NULL jeżeli znak nie został znaleziony). Chodzi o to, aby zastąpić każdą nową linię na końcu każdej linii, \0tak aby nowa linia nigdy nie była liczona przez strlen(): jest to tak, że długość zawsze można porównać do 6, niezależnie od potencjalnie brakującej nowej linii w ostatniej linii. Wiem, że inne traktowanie tylko ostatniej linii byłoby znacznie bardziej wydajne. Prawdopodobnie zaktualizuję to później.
greprozwiązanie dla tego samego pliku i jest ono rzeczywiście szybsze (prawdopodobnie dlatego, że strlen()nie jest to najlepszy pomysł tutaj) . Spróbuję użyć getchar()pętli, aby zamiast tego sprawdzić tylko pierwszą literę N, myślę, że powinna to wyraźnie poprawić. I tak, każda linia powyżej długości bufora jest po prostu przycięta do długości bufora.
Możesz używać Vima w trybie Ex:
ex -sc 'v/\v.{6}/d' -cx file
\v włącz magię
.{6} znajdź wiersze z co najmniej 6 znakami
v Odwróć wybór
d usunąć
x Zapisz i zamknij
Rozwiązanie Ruby:
$ cat input.txt
abcdef
abc
abcdefghijk
$ ruby -ne 'puts $_ if $_.chomp.length() >= 6 ' < input.txt
abcdef
abcdefghijk
Prosty pomysł: przekieruj plik do standardowego ruby i wypisz linię ze standardowego, tylko jeśli jego długość jest większa lub równa 6