¿Cómo puedo contar el número de caracteres diferentes en un archivo?

19

Necesitaría un programa, que muestre el número de los diferentes caracteres en un archivo. Ejemplo:

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

Existe alguna herramienta, que haga esto?

Mnementh
fuente

Respuestas:

21

Lo siguiente debería funcionar:

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

Primero, insertamos una nueva línea después de cada carácter, poniendo cada carácter en su propia línea. Luego lo clasificamos. Luego usamos el comando uniq para eliminar los duplicados, prefijando cada línea con el número de ocurrencias de ese carácter.

Para ordenar la lista por frecuencia, canalice todo esto en sort -nr.

Steven D
fuente
44
En sed para Mac OS X essed 's/\(.\)/\1\'$'\n/g' text.txt
mb21
Muy bueno, pero desafortunadamente no funciona correctamente si el texto contiene caracteres Unicode (utf8). Puede haber una manera de hacer sedesto, pero la solución Python de Jacob Vlijm funcionó bien para mí.
bitinerante
14

La solución de Steven es buena y simple. No es tan eficaz para archivos muy grandes (archivos que no caben cómodamente en aproximadamente la mitad de su RAM) debido al paso de clasificación. Aquí hay una versión awk. También es un poco más complicado, ya que trata de hacer lo correcto para algunos caracteres especiales (saltos de línea, ', \, :).

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/:/'

Aquí hay una solución de Perl con el mismo principio. Perl tiene la ventaja de poder clasificar internamente. Además, esto no contará correctamente una nueva línea adicional si el archivo no termina en un carácter de nueva línea.

perl -ne '
  ++$c{$_} foreach split //;
  END { printf "'\''%s'\'': %d\n", /[\\'\'']/ ? "\\$_" : /./ ? $_ : "\\n", $c{$_}
        foreach (sort {$c{$b} <=> $c{$a}} keys %c) }'
Gilles 'SO- deja de ser malvado'
fuente
1
+1 por no hacer ese tipo horrible
Sparr
1

Una versión lenta pero relativamente amigable con la memoria, que usa ruby. Alrededor de una docena de MB de RAM, independientemente del tamaño de 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
fuente