Apakah ada cara mudah untuk menghitung karakter dalam kata-kata dalam file, dari terminal?

8

Saya memiliki 100 juta baris dalam file saya.

Setiap baris hanya memiliki satu kolom.

misalnya

aaaaa
bb
cc
ddddddd
ee

Saya ingin mendaftar jumlah karakter

Seperti ini

2 character words - 3
5 character words - 1
7 character words - 1

dll.

Apakah ada cara mudah untuk melakukan ini di terminal?

Giri
sumber

Jawaban:

20
$ awk '{ print length }' file | sort -n | uniq -c | awk '{ printf("%d character words: %d\n", $2, $1) }'
2 character words: 3
5 character words: 1
7 character words: 1

awkFilter pertama hanya akan mencetak panjang setiap baris dalam file yang dipanggil file. Saya berasumsi bahwa file ini mengandung satu kata per baris.

The sort -n(mengurutkan garis dari output secara awknumerik dalam urutan menaik) dan uniq -c(menghitung berapa kali setiap baris muncul secara berurutan) kemudian akan membuat output berikut dari itu untuk data yang diberikan:

   3 2
   1 5
   1 7

Ini kemudian diuraikan oleh awkskrip kedua yang menafsirkan setiap baris sebagai "X jumlah baris yang memiliki karakter Y" dan menghasilkan output yang diinginkan.


Solusi alternatif adalah melakukan semuanya dalam awkdan menjaga jumlah panjang dalam array. Ini adalah tradeoff antara efisiensi, keterbacaan / kemudahan pemahaman (dan karenanya pemeliharaan) solusi mana yang "terbaik".

Solusi alternatif:

$ awk '{ len[length]++ } END { for (i in len) printf("%d character words: %d\n", i, len[i]) }' file
2 character words: 3
5 character words: 1
7 character words: 1
Kusalananda
sumber
Tidak perlu mengurutkan awk (array yang diindeks secara numerik diurutkan secara default) (lebih cepat)
Isaac
@Arrow saya tahu. Saya punya solusi yang berkomentar dalam jawaban saya karena Sundeep mengalahkan saya untuk itu dengan beberapa detik. Saya juga menyinggung ini dengan paragraf terakhir saya.
Kusalananda
Saya percaya komentar harus bermanfaat bagi pengguna solusi (tidak termasuk dalam jawaban Anda (atau Sundeep) :-) ...). Kalau tidak: sertakan komentar dengan efek yang sama dalam jawaban Anda dan saya dengan senang hati akan menghapus komentar saya. :-)
Isaac
10

Cara lain untuk melakukan semuanya awksendirian

$ awk '{words[length()]++} END{for(k in words)print k " character words - " words[k]}' ip.txt 
2 character words - 3
5 character words - 1
7 character words - 1
  • words[length()]++ gunakan panjang jalur input sebagai kunci untuk menghemat penghitungan
  • END{for(k in words)print k " character words - " words[k]} setelah semua baris diproses, cetak konten array dalam format yang diinginkan


Perbandingan kinerja, angka yang dipilih adalah yang terbaik dari dua kali proses

$ wc words.txt
 71813  71813 655873 words.txt
$ perl -0777 -ne 'print $_ x 1000' words.txt > long_file.txt
$ du -h --apparent-size long_file.txt
626M    long_file.txt

$ time awk '{words[length()]++} END{for(k in words)print k " character words - " words[k]}' long_file.txt > t1

real    0m20.632s
user    0m20.464s
sys     0m0.108s

$ time perl -lne '$h{length($_)}++ }{ for $n (sort keys %h) {print "$n character words - $h{$n}"}' long_file.txt > t2

real    0m19.749s
user    0m19.640s
sys     0m0.108s

$ time awk '{ print length }' long_file.txt | sort -n | uniq -c | awk '{ printf("%d character words - %d\n", $2, $1) }' > t3

real    1m23.294s
user    1m24.952s
sys     0m1.980s

$ diff -s <(sort t1) <(sort t2)
Files /dev/fd/63 and /dev/fd/62 are identical
$ diff -s <(sort t1) <(sort t3)
Files /dev/fd/63 and /dev/fd/62 are identical

Jika file hanya memiliki karakter ASCII,

$ time LC_ALL=C awk '{words[length()]++} END{for(k in words)print k " character words - " words[k]}' long_file.txt > t1

real    0m15.651s
user    0m15.496s
sys     0m0.120s

Tidak yakin mengapa waktu untuk perltidak banyak berubah, mungkin pengodean harus diatur dengan cara lain

Sundeep
sumber
Saya baru saja menambahkan itu ke solusi saya sendiri. Dihapus ketika saya melihat milikmu sekalipun. :-)
Kusalananda
ya saya sedang berdebat untuk menghapus milik saya sebelum melihat hasil edit Anda lagi :)
Sundeep
Tidak perlu mengurutkan array yang diindeks secara numerik . Itu selalu dipesan dengan indeks yang meningkat. (well, setidaknya dalam awk :-))
Isaac
lengthtanpa ()bekerja dengan baik di sini, jadi mungkin berlebihan untuk menambahkan kawat gigi. Saya menggunakan GNU awk.
Sergiy Kolodyazhnyy
2
@SergiyKolodyazhnyy ya, manual gnu awk mengatakanIn older versions of awk, the length() function could be called without any parentheses. Doing so is considered poor practice, although the 2008 POSIX standard explicitly allows it, to support historical practice. For programs to be maximally portable, always supply the parentheses
Sundeep
5

Berikut ini adalah yang perlsetara (dengan - opsional - urutkan):

$ perl -lne '
    $h{length($_)}++ }{ for $n (sort keys %h) {print "$n character words - $h{$n}"}
' file
2 character words - 3
5 character words - 1
7 character words - 1
Steeldriver
sumber
Jika indeks kunci numerik: Apakah array kunci perlu diurutkan dalam Perl?
Isaac
1
@Arrow: Jawaban ini menggunakan hash (yaitu array asosiatif dengan kunci string), dan mereka memiliki urutan kunci yang tidak ditentukan, jadi ya. Bahkan, jawabannya agak buggy karena itu menyortir kunci sebagai string, bukan sebagai angka. Menambahkan {$a<=>$b}setelah sortakan memperbaikinya. Atau, seseorang dapat menggunakan array normal dengan tombol numerik dan lewati saja sembarang tombol yang nilainya nol / tidak terdefinisi.
Ilmari Karonen
@IlmariKaronen Terima kasih, lebih baik sekarang. Apa yang membuat perbedaan kurung kurawal !!
Isaac
Akan lebih efisien untuk menggunakan array daripada hash. OP menginginkan jutaan garis, sehingga setiap overhead memeriksa dan melompati nol saat mencetak mudah dilakukan dengan pengindeksan yang lebih murah.
Peter Cordes
5

Alternatif satu panggilan ke GNU awk, menggunakan printf :

$ awk 'BEGIN { PROCINFO["sorted_in"] = "@ind_str_asc"}
       {c[length($0)]++}
       END{
           for(i in c){printf("%s character words - %s\n",i,c[i])}
          }' infile
2 character words - 3
5 character words - 1
7 character words - 1

Algoritma inti hanya mengumpulkan jumlah karakter dalam array. Bagian akhir mencetak jumlah yang dikumpulkan yang diformat dengan printf.

Cepat, sederhana, satu panggilan tunggal untuk awk.

Lebih tepatnya: lebih banyak memori digunakan untuk menyimpan array.
Tetapi tidak ada jenis yang disebut (indeks array numerik diatur untuk selalu dilalui diurutkan ke atas dengan PROCINFO), dan hanya satu program eksternal:, awkbukan beberapa.

Ishak
sumber
1
for inmungkin terjadi untuk memberikan indeks array numerik dalam urutan numerik setidaknya untuk beberapa nilai atau dalam beberapa implementasi awk, tetapi itu tidak diperlukan, tidak tradisional, dan jelas tidak universal. Itu sering terjadi untuk set kecil seperti 2 atau 3 atau mungkin 4; coba 10 atau 20 pada setiap awk Anda memiliki akses ke (tanpa PROCINFO atau WHINY_USERS di gawk) dan saya yakin $ 50 setidaknya satu kasus tidak diurutkan.
dave_thompson_085
Terima kasih atas masukan Anda. Menggunakan ini : Saya yakin ini sudah disortir sekarang. :-)
Isaac
1
@ind_str_ascmengurutkan sebagai string, yang akan benar untuk angka hanya jika mereka semua satu digit (seperti contoh Anda); gunakan @ind_num_ascjika (ada) nilai bisa 10 atau lebih. Dan meskipun itu kurang masalah sekarang daripada dulu, fitur ini hanya melongo 4,0 .
dave_thompson_085