Comment puis-je compter le nombre de caractères différents dans un fichier?

19

J'aurais besoin d'un programme qui génère le nombre de caractères différents dans un fichier. Exemple:

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

Existe un outil, qui fait ça?

Mnementh
la source

Réponses:

21

Les éléments suivants devraient fonctionner:

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

Tout d'abord, nous insérons une nouvelle ligne après chaque caractère, en mettant chaque caractère sur sa propre ligne. Ensuite, nous le trions. Ensuite, nous utilisons la commande uniq pour supprimer les doublons, en préfixant chaque ligne avec le nombre d'occurrences de ce caractère.

Pour trier la liste par fréquence, canalisez tout cela dans sort -nr.

Steven D
la source
4
Sur sed pour Mac OS X c'estsed 's/\(.\)/\1\'$'\n/g' text.txt
mb21
Très bien, mais malheureusement cela ne fonctionne pas correctement si le texte contient des caractères Unicode (utf8). Il y a peut-être un moyen de le faire sed, mais la solution Python de Jacob Vlijm a bien fonctionné pour moi.
bitinerant
14

La solution de Steven est bonne et simple. Ce n'est pas si performant pour les très gros fichiers (les fichiers qui ne tiennent pas confortablement dans environ la moitié de votre RAM) en raison de l'étape de tri. Voici une version awk. Il est aussi un peu plus compliqué , car il essaie de faire la bonne chose pour quelques caractères spéciaux (sauts de ligne, ', \, :).

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

Voici une solution Perl sur le même principe. Perl a l'avantage de pouvoir trier en interne. De plus, cela ne comptera pas correctement une nouvelle ligne supplémentaire si le fichier ne se termine pas par un caractère de nouvelle ligne.

perl -ne '
  ++$c{$_} foreach split //;
  END { printf "'\''%s'\'': %d\n", /[\\'\'']/ ? "\\$_" : /./ ? $_ : "\\n", $c{$_}
        foreach (sort {$c{$b} <=> $c{$a}} keys %c) }'
Gilles 'SO- arrête d'être méchant'
la source
1
+1 pour ne pas avoir fait ce genre de chose horrible
Sparr
1

Une version lente mais relativement conviviale, utilisant du rubis. Environ une douzaine de Mo de RAM, quelle que soit la taille d'entrée.

# 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
la source