Como faço para remover todas as linhas em um arquivo com menos de 6 caracteres?

17

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?

Diga-me o porquê
fonte
Esta pergunta não é mais adequada para o Stackoverflow?
user1073075
2
@ user1073075 é perfeitamente tópico aqui.
Seth

Respostas:

30

Há muitas maneiras de fazer isso.

Usando grep:

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

Agora out.txtconterá 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 grepou editar o arquivo no local usando a -iopção de sed:

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

O backup do arquivo original será feito file.txt.bake 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')
heemail
fonte
Yay! Eu estava esperando por uma resposta python =)
TellMeWhy
@DevRobot I see..then confira compreensão da lista i adicionado, seja mais Pythonic ..
heemayl
1
Além disso, o @DevRobot não tem tanta certeza de que o python é mais lento em arquivos grandes quando a primeira opção é usada. Na verdade, tenho certeza de que o python é mais rápido em milhões de linhas, uma vez que lê por linha.
Jacob Vlijm
1
O segundo exemplo de python lê o arquivo inteiro na memória antes de fazer a junção. Eu acho que o primeiro exemplo de python é melhor nesse caso.
Holloway
A leitura por linhas é necessariamente mais lenta porque os arquivos não são estruturados dessa maneira. Você precisa ler um bloco à frente de qualquer maneira e procurar uma nova linha com possibilidades reduzidas de paralelização, depois retornar apenas a string parcial. Você precisa de um buffer circular. Você precisa alocar memória dinamicamente se não souber quanto tempo as linhas podem ser.
The Vee
19

É muito simples:

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

Isso é extremamente eficiente, pois grepnã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.

Olivier Dulac
fonte
14

Solução 1: usando C

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 1000000caracteres por linha; você pode alterar isso alterando o valor de MAX_BUFFER_SIZE).

(Truque para substituir \npor \0encontrado 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

Solução # 2: usando AWK:

awk 'length>=6' file
  • length>=6: se length>=6retornar VERDADEIRO, imprime o registro atual.

Solução # 3: usando Perl:

perl -lne 'length>=6&&print' file
  • Se lenght>=6retornar 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
kos
fonte
1
Acredite em mim .. awk
eu
2
@heemayl E eu não vi a pergunta imediatamente, então sabia que, se você estivesse online, teria sido mais rápido. Tive que excluir minha sedsolução (acontece, eu sei). XD
kos
Qual é o ponto da posvariável? Eu entendo que retorna um ponteiro para o personagem linecom um caractere de nova linha, mas você nunca parece usá-lo. E se você não encontrar, basta configurá-lo para \0.
precisa saber é o seguinte
@ user1717828 Se o encontrar , substituo-o por \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, \0para 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.
kos
1
@tripleee A idéia era adicionar uma solução útil para algo mais do que um trabalho único ou para arquivos ainda maiores, mas : testei a grepsoluçã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.
kos
2

Você pode usar o Vim no modo Ex:

ex -sc 'v/\v.{6}/d' -cx file
  1. \v ligue magia

  2. .{6} encontre linhas com 6 ou mais caracteres

  3. v seleção invertida

  4. d excluir

  5. x salvar e fechar

Steven Penny
fonte
1

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

Sergiy Kolodyazhnyy
fonte