Como posso contar o número de caracteres diferentes em um arquivo?

19

Eu precisaria de um programa que produza o número de caracteres diferentes em um arquivo. Exemplo:

> stats testfile
' ': 207
'e': 186
'n': 102

Existe alguma ferramenta que faça isso?

Mnementh
fonte

Respostas:

21

O seguinte deve funcionar:

$ sed 's/\(.\)/\1\n/g' text.txt | sort | uniq -c

Primeiro, inserimos uma nova linha após cada caractere, colocando cada caractere em sua própria linha. Então nós resolvemos isso. Em seguida, usamos o comando uniq para remover as duplicatas, prefixando cada linha com o número de ocorrências desse caractere.

Para classificar a lista por frequência, canalize tudo isso sort -nr.

Steven D
fonte
4
No sed para Mac OS X, ésed 's/\(.\)/\1\'$'\n/g' text.txt
mb21
Muito bom, mas infelizmente não funcionará corretamente se o texto contiver caracteres Unicode (utf8). Pode haver uma maneira de sedfazer isso, mas a solução Python de Jacob Vlijm funcionou bem para mim.
bitinerant
14

A solução de Steven é boa e simples. Não é tão eficiente para arquivos muito grandes (arquivos que não cabem confortavelmente em cerca de metade da sua RAM) devido à etapa de classificação. Aqui está uma versão awk. É também um pouco mais complicado, porque ele tenta fazer a coisa certa por alguns caracteres especiais (novas linhas, ', \, :).

awk '
  {for (i=1; i<=length; i++) ++c[substr($0,i,1)]; ++c[RS]}
  function chr (x) {return x=="\n" ? "\\n" : x==":" ? "\\072" :
                           x=="\\" || x=="'\''" ? "\\" x : x}
  END {for (x in c) printf "'\''%s'\'': %d\n", chr(x), c[x]}
' | sort -t : -k 2 -r | sed 's/\\072/:/'

Aqui está uma solução Perl com o mesmo princípio. Perl tem a vantagem de poder classificar internamente. Além disso, isso não contará corretamente uma nova linha extra se o arquivo não terminar em um caractere de nova linha.

perl -ne '
  ++$c{$_} foreach split //;
  END { printf "'\''%s'\'': %d\n", /[\\'\'']/ ? "\\$_" : /./ ? $_ : "\\n", $c{$_}
        foreach (sort {$c{$b} <=> $c{$a}} keys %c) }'
Gilles 'SO- parar de ser mau'
fonte
1
+1 por não fazer esse tipo horrível
Sparr
1

Uma versão lenta, mas relativamente amiga da memória, usando ruby. Cerca de uma dúzia de MB de RAM, independentemente do tamanho da entrada.

# count.rb
ARGF.
  each_char.
  each_with_object({}) {|e,a| a[e] ||= 0; a[e] += 1}.
  each {|i| puts i.join("\t")}

ruby count.rb < input.txt
t       20721
d       20628
S       20844
k       20930
h       20783
... etc
Jared Beck
fonte