Existe uma maneira fácil de contar caracteres em palavras no arquivo, do terminal?

8

Eu tenho 100 milhões de linhas no meu arquivo.

Cada linha tem apenas uma coluna.

por exemplo,

aaaaa
bb
cc
ddddddd
ee

Eu gostaria de listar a contagem de caracteres

Como isso

2 character words - 3
5 character words - 1
7 character words - 1

etc.

Existe alguma maneira fácil de fazer isso no terminal?

    
por user1091558 08.10.2017 / 17:38

4 respostas

20
$ awk '{ print length }' file | sort -n | uniq -c | awk '{ printf("%d character words: %d\n", $2, $1) }'
2 character words: 3
5 character words: 1
7 character words: 1

O primeiro filtro awk imprimirá apenas o comprimento de cada linha no arquivo chamado file . Estou assumindo que este arquivo contém uma palavra por linha.

O sort -n (classificar as linhas da saída de awk numericamente em ordem crescente) e uniq -c (contar o número de vezes que cada linha ocorre consecutivamente) criará a seguinte saída daquela para os dados fornecidos :

   3 2
   1 5
   1 7

Isso é então analisado pelo segundo script awk , que interpreta cada linha como "número X de linhas com caracteres Y" e produz a saída desejada.

A solução alternativa é fazer tudo em awk e manter contagens de comprimentos em uma matriz. É uma troca entre eficiência, legibilidade / facilidade de entendimento (e, portanto, manutenibilidade), que solução é a "melhor".

Solução alternativa:

$ awk '{ len[length]++ } END { for (i in len) printf("%d character words: %d\n", i, len[i]) }' file
2 character words: 3
5 character words: 1
7 character words: 1
    
por 08.10.2017 / 17:43
11

Outra maneira de fazer tudo isso com awk sozinho

$ awk '{words[length()]++} END{for(k in words)print k " character words - " words[k]}' ip.txt 
2 character words - 3
5 character words - 1
7 character words - 1
  • words[length()]++ usa o comprimento da linha de entrada como chave para economizar contagem
  • END{for(k in words)print k " character words - " words[k]} depois que todas as linhas são processadas, imprima o conteúdo da matriz no formato desejado


Comparação de desempenho, os números selecionados são os melhores de duas execuções

$ wc words.txt
 71813  71813 655873 words.txt
$ perl -0777 -ne 'print $_ x 1000' words.txt > long_file.txt
$ du -h --apparent-size long_file.txt
626M    long_file.txt

$ time awk '{words[length()]++} END{for(k in words)print k " character words - " words[k]}' long_file.txt > t1

real    0m20.632s
user    0m20.464s
sys     0m0.108s

$ time perl -lne '$h{length($_)}++ }{ for $n (sort keys %h) {print "$n character words - $h{$n}"}' long_file.txt > t2

real    0m19.749s
user    0m19.640s
sys     0m0.108s

$ time awk '{ print length }' long_file.txt | sort -n | uniq -c | awk '{ printf("%d character words - %d\n", $2, $1) }' > t3

real    1m23.294s
user    1m24.952s
sys     0m1.980s

$ diff -s <(sort t1) <(sort t2)
Files /dev/fd/63 and /dev/fd/62 are identical
$ diff -s <(sort t1) <(sort t3)
Files /dev/fd/63 and /dev/fd/62 are identical

Se o arquivo tiver apenas caracteres ASCII,

$ time LC_ALL=C awk '{words[length()]++} END{for(k in words)print k " character words - " words[k]}' long_file.txt > t1

real    0m15.651s
user    0m15.496s
sys     0m0.120s

Não sei porque o tempo para perl não mudou muito, provavelmente a codificação tem que ser definida de outra forma

    
por 08.10.2017 / 17:59
5

Aqui está um perl equivalente (com - opcional - classificação):

$ perl -lne '
    $h{length($_)}++ }{ for $n (sort keys %h) {print "$n character words - $h{$n}"}
' file
2 character words - 3
5 character words - 1
7 character words - 1
    
por 08.10.2017 / 18:50
5

Uma alternativa uma chamada para o GNU awk, usando printf :

$ awk 'BEGIN { PROCINFO["sorted_in"] = "@ind_str_asc"}
       {c[length($0)]++}
       END{
           for(i in c){printf("%s character words - %s\n",i,c[i])}
          }' infile
2 character words - 3
5 character words - 1
7 character words - 1

O algoritmo core apenas coleta contagens de caracteres em uma matriz. A parte final imprime as contagens coletadas formatadas com printf.

Rápido, simples, uma única ligação para o awk.

Para ser mais preciso: mais memória é usada para manter o array.
Mas nenhuma classificação é chamada (os índices de matrizes numéricas são definidos para serem sempre percorridos classificados para cima com PROCINFO) e apenas um programa externo: awk , em vez de vários.

    
por 08.10.2017 / 19:55