Odpowiedzi:
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
.
sed
, ale rozwiązanie Python Jacoba Vlijma działało dla mnie dobrze.
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) }'
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
sed 's/\(.\)/\1\'$'\n/g' text.txt