Jak mogę policzyć liczbę różnych znaków w pliku?

19

Potrzebowałbym programu, który wyświetla liczbę różnych znaków w pliku. Przykład:

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

Czy istnieje jakieś narzędzie, które to robi?

Memento
źródło

Odpowiedzi:

21

Następujące powinny działać:

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

Najpierw wstawiamy nowy wiersz po każdym znaku, umieszczając każdy znak w osobnej linii. Potem to sortujemy. Następnie używamy polecenia uniq, aby usunąć duplikaty, poprzedzając każdą linię liczbą wystąpień tego znaku.

Aby posortować listę według częstotliwości, umieść to wszystko w potoku sort -nr.

Steven D.
źródło
4
Na sed dla Mac OS X jestsed 's/\(.\)/\1\'$'\n/g' text.txt
mb21,
Bardzo fajnie, ale niestety nie działa poprawnie, jeśli tekst zawiera znaki Unicode (utf8). Może być na to sposób sed, ale rozwiązanie Python Jacoba Vlijma działało dla mnie dobrze.
bitinerant
14

Rozwiązanie Stevena jest dobre, proste. Nie jest tak wydajny w przypadku bardzo dużych plików (plików, które nie mieszczą się wygodnie w około połowie pamięci RAM) z powodu kroku sortowania. Oto wersja awk. Jest to także trochę bardziej skomplikowane, ponieważ stara się zrobić dobry uczynek dla kilku znaków specjalnych (znaki nowej linii, ', \, :).

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

Oto rozwiązanie Perla na tej samej zasadzie. Perl ma tę zaletę, że może sortować wewnętrznie. Również to nie będzie poprawnie liczyć dodatkowej nowej linii, jeśli plik nie kończy się znakiem nowej linii.

perl -ne '
  ++$c{$_} foreach split //;
  END { printf "'\''%s'\'': %d\n", /[\\'\'']/ ? "\\$_" : /./ ? $_ : "\\n", $c{$_}
        foreach (sort {$c{$b} <=> $c{$a}} keys %c) }'
Gilles „SO- przestań być zły”
źródło
1
+1 za nie robienie tego okropnego rodzaju
Sparr
1

Wolna, ale stosunkowo przyjazna dla pamięci wersja, wykorzystująca ruby. Około tuzina MB pamięci RAM, niezależnie od wielkości wejściowej.

# 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
źródło