Ho un file contenente circa 10 milioni di righe.
Voglio rimuovere tutte le righe nel file che sono meno di sei caratteri.
Come faccio a fare questo?
Ho un file contenente circa 10 milioni di righe.
Voglio rimuovere tutte le righe nel file che sono meno di sei caratteri.
Come faccio a fare questo?
Risposte:
Ci sono molti modi per farlo.
Utilizzando grep
:
grep -E '^.{6,}$' file.txt >out.txt
Ora out.txt
conterrà righe con sei o più caratteri.
Modo inverso:
grep -vE '^.{,5}$' file.txt >out.txt
Utilizzo sed
, rimozione di linee di lunghezza 5 o inferiore:
sed -r '/^.{,5}$/d' file.txt
Modo inverso, linee di stampa di lunghezza sei o più:
sed -nr '/^.{6,}$/p' file.txt
È possibile salvare l'output in un altro file utilizzando un >
operatore simile grep
o modificare il file sul posto utilizzando l' -i
opzione di sed
:
sed -ri.bak '/^.{6,}$/' file.txt
Verrà eseguito il backup del file originale così come file.txt.bak
lo sarà il file modificato file.txt
.
Se non si desidera conservare un backup:
sed -ri '/^.{6,}$/' file.txt
Utilizzando shell, Slower, Non farlo , questo è solo per il gusto di mostrare un altro metodo:
while IFS= read -r line; do [ "${#line}" -ge 6 ] && echo "$line"; done <file.txt
Utilizzando python
, anche più lento di 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')
Meglio usare la comprensione dell'elenco per essere più Pythonic:
#!/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')
È molto semplice:
grep ...... inputfile > resultfile #There are 6 dots
Questo è estremamente efficiente, in quanto grep
non tenterà di analizzare più del necessario, né di interpretare i caratteri in alcun modo: invia semplicemente una (intera) linea a stdout (che la shell reindirizza quindi al file dei risultati) non appena ha visto 6 caratteri su quella riga ( .
in un contesto regexp corrisponde a 1 carattere qualsiasi).
Quindi grep produrrà solo linee con 6 (o più) caratteri, e gli altri non vengono emessi da grep in modo che non riescano a ottenere il risultato.
Il modo più veloce: compilare ed eseguire questo programma 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;
}
Compila gcc program.c -o program
, esegui con ./program file line_length
(dove file
= percorso del file e line_length
= lunghezza minima della riga, nel tuo caso6
; la lunghezza massima della riga è limitata ai 1000000
caratteri per riga; puoi cambiarla cambiando il valore di MAX_BUFFER_SIZE
).
(Trucco per sostituire \n
con \0
trovato qui .)
Confronto con tutte le altre soluzioni proposte a questa domanda tranne la soluzione shell (test eseguito su un file di ~ 91MB con linee da 10M con una lunghezza media di 8 caratteri):
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
: se length>=6
restituisce VERO, stampa il record corrente.perl -lne 'length>=6&&print' file
lenght>=6
restituisce VERO, stampa il record corrente.% 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
soluzione ...
sed
soluzione (succede, lo so). XD
pos
variabile? Capisco che restituisce un puntatore al personaggio line
con un carattere newline, ma sembra che non lo usi mai. E se non lo trovi, lo imposti a uguale \0
.
\0
( strchr()
restituisce un puntatore NULL se il carattere non viene trovato). Il punto sta sostituendo ogni nuova riga alla fine di ogni riga in \0
modo che la nuova riga non venga mai conteggiata da strlen()
: questo è così che la lunghezza può sempre essere confrontata con 6 indipendentemente da una potenziale nuova riga mancante nell'ultima riga. Trattare diversamente solo l'ultima riga sarebbe molto più efficiente, lo so. Probabilmente lo aggiornerò più tardi.
grep
soluzione sullo stesso file ed è effettivamente più veloce (probabilmente perché strlen()
non è la migliore idea qui) . Proverò ad usare un getchar()
loop per controllare invece solo il primo carattere N, suppongo che dovrebbe migliorarlo visibilmente. E sì, qualsiasi linea sulla lunghezza del buffer viene semplicemente tagliata alla lunghezza del buffer.
Puoi usare Vim in modalità Ex:
ex -sc 'v/\v.{6}/d' -cx file
\v
accendi la magia
.{6}
trova righe con 6 o più caratteri
v
inverti selezione
d
Elimina
x
salva e chiudi
Soluzione ruby:
$ cat input.txt
abcdef
abc
abcdefghijk
$ ruby -ne 'puts $_ if $_.chomp.length() >= 6 ' < input.txt
abcdef
abcdefghijk
Idea semplice: reindirizzare il file nello stdin di Ruby e stampare la linea dallo stdin solo se è maggiore o uguale a 6