Bagaimana saya bisa menghitung jumlah karakter yang berbeda dalam suatu file?

19

Saya membutuhkan program, yang menampilkan jumlah karakter yang berbeda dalam sebuah file. Contoh:

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

Ada alat apa pun, yang melakukan ini?

Mnementh
sumber

Jawaban:

21

Berikut ini harus bekerja:

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

Pertama, kami menyisipkan baris baru setelah setiap karakter, menempatkan setiap karakter pada barisnya masing-masing. Lalu kami mengatasinya. Kemudian kami menggunakan perintah uniq untuk menghapus duplikat, mengawali setiap baris dengan jumlah kemunculan karakter itu.

Untuk mengurutkan daftar berdasarkan frekuensi, masukkan semua ini ke dalam sort -nr.

Steven D
sumber
4
Pada sed untuk Mac OS X itused 's/\(.\)/\1\'$'\n/g' text.txt
mb21
Sangat bagus, tetapi sayangnya itu tidak berfungsi dengan baik jika teks berisi karakter Unicode (utf8). Mungkin ada cara untuk sedmelakukan ini, tetapi solusi Python Jacob Vlijm bekerja dengan baik untuk saya.
bitinerant
14

Solusi Steven adalah solusi yang bagus dan sederhana. Ini tidak begitu performan untuk file yang sangat besar (file yang tidak pas dengan nyaman di sekitar setengah RAM Anda) karena langkah penyortiran. Ini versi awk. Ini juga sedikit lebih rumit karena mencoba untuk melakukan hal yang benar untuk beberapa karakter khusus (baris baru, ', \, :).

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

Berikut adalah solusi Perl pada prinsip yang sama. Perl memiliki keuntungan karena dapat memilah secara internal. Juga ini dengan benar tidak akan menghitung baris baru tambahan jika file tidak berakhir dengan karakter baris baru.

perl -ne '
  ++$c{$_} foreach split //;
  END { printf "'\''%s'\'': %d\n", /[\\'\'']/ ? "\\$_" : /./ ? $_ : "\\n", $c{$_}
        foreach (sort {$c{$b} <=> $c{$a}} keys %c) }'
Gilles 'SANGAT berhenti menjadi jahat'
sumber
1
+1 karena tidak melakukan hal yang mengerikan
Sparr
1

Versi lambat tapi relatif ramah memori, menggunakan ruby. Tentang selusin MB RAM, terlepas dari ukuran input.

# 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
sumber