Eu tenho um arquivo contendo aproximadamente 10 milhões de linhas.
Quero remover todas as linhas do arquivo com menos de seis caracteres.
Como eu faço isso?
Eu tenho um arquivo contendo aproximadamente 10 milhões de linhas.
Quero remover todas as linhas do arquivo com menos de seis caracteres.
Como eu faço isso?
Respostas:
Há muitas maneiras de fazer isso.
Usando grep
:
grep -E '^.{6,}$' file.txt >out.txt
Agora out.txt
conterá linhas com seis ou mais caracteres.
Maneira reversa:
grep -vE '^.{,5}$' file.txt >out.txt
Usando sed
, removendo linhas de comprimento 5 ou menos:
sed -r '/^.{,5}$/d' file.txt
De maneira inversa, imprimindo linhas de comprimento seis ou mais:
sed -nr '/^.{6,}$/p' file.txt
Você pode salvar a saída em um arquivo diferente usando o >
operador como grep
ou editar o arquivo no local usando a -i
opção de sed
:
sed -ri.bak '/^.{6,}$/' file.txt
O backup do arquivo original será feito file.txt.bak
e o arquivo modificado file.txt
.
Se você não deseja manter um backup:
sed -ri '/^.{6,}$/' file.txt
Usando shell, Slower, não faça isso , é apenas para mostrar outro método:
while IFS= read -r line; do [ "${#line}" -ge 6 ] && echo "$line"; done <file.txt
Usando python
, ainda mais lento do que 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')
Melhor compreensão da lista de uso para ser mais pitônico:
#!/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')
É muito simples:
grep ...... inputfile > resultfile #There are 6 dots
Isso é extremamente eficiente, pois grep
não tentará analisar mais do que o necessário, nem interpretar os caracteres de forma alguma: simplesmente envia uma linha (inteira) para stdout (que o shell redireciona para o arquivo de resultados) assim que vê 6 caracteres nessa linha ( .
em um contexto regexp corresponde a qualquer caractere 1).
Portanto, o grep produzirá apenas linhas com 6 (ou mais) caracteres, e os outros não serão gerados pelo grep, de forma que não resultem no arquivo de resultados.
Maneira mais rápida: compile e execute este programa 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;
}
Compile com gcc program.c -o program
, execute com ./program file line_length
(where file
= caminho para o arquivo e line_length
= comprimento mínimo da linha, no seu caso 6
; o comprimento máximo da linha é limitado a 1000000
caracteres por linha; você pode alterar isso alterando o valor de MAX_BUFFER_SIZE
).
(Truque para substituir \n
por \0
encontrado aqui .)
Comparação com todas as outras soluções propostas para essa pergunta, exceto a solução shell (teste executado em um arquivo de ~ 91 MB com 10 milhões de linhas com um comprimento médio de 8 caracteres):
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
retornar VERDADEIRO, imprime o registro atual.perl -lne 'length>=6&&print' file
lenght>=6
retornar TRUE, imprime o registro atual.% 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
sed
solução (acontece, eu sei). XD
pos
variável? Eu entendo que retorna um ponteiro para o personagem line
com um caractere de nova linha, mas você nunca parece usá-lo. E se você não encontrar, basta configurá-lo para \0
.
\0
( strchr()
retorna um ponteiro NULL se o caractere não for encontrado). O ponto é substituir cada nova linha no final de cada linha por, \0
para que a nova linha nunca seja contada por strlen()
: isto é, para que o comprimento sempre possa ser comparado a 6, independentemente de uma nova linha em falta na última linha. Tratar de maneira diferente apenas a última linha seria muito mais eficiente, eu sei. Provavelmente vou atualizar isso mais tarde.
grep
solução no mesmo arquivo e é realmente mais rápido (provavelmente porque strlen()
não é a melhor ideia aqui) . Vou tentar usar um getchar()
loop para verificar apenas o primeiro caractere N, acho que isso deve melhorar visivelmente. E sim, qualquer linha acima do comprimento do buffer é simplesmente cortada no comprimento do buffer.
Você pode usar o Vim no modo Ex:
ex -sc 'v/\v.{6}/d' -cx file
\v
ligue magia
.{6}
encontre linhas com 6 ou mais caracteres
v
seleção invertida
d
excluir
x
salvar e fechar
Solução Ruby:
$ cat input.txt
abcdef
abc
abcdefghijk
$ ruby -ne 'puts $_ if $_.chomp.length() >= 6 ' < input.txt
abcdef
abcdefghijk
Idéia simples: redirecione o arquivo para o stdin do ruby e imprima a linha do stdin apenas se o comprimento for maior ou igual a 6