Có một cách dễ dàng để đếm các ký tự trong các từ trong tệp, từ thiết bị đầu cuối?


8

Tôi có 100 triệu hàng trong tệp của mình.

Mỗi hàng chỉ có một cột.

ví dụ

aaaaa
bb
cc
ddddddd
ee

Tôi muốn liệt kê số lượng nhân vật

Như thế này

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

Vân vân.

Có cách nào dễ dàng để làm điều này trong thiết bị đầu cuối?


Câu trả lời:


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

Bộ awklọc đầu tiên sẽ chỉ in độ dài của mỗi dòng trong tệp được gọi file. Tôi giả sử rằng tệp này chứa một từ trên mỗi dòng.

Các sort -n(loại các dòng từ đầu ra của awksố lượng trong thứ tự tăng dần) và uniq -c(đếm số lần mỗi dòng xảy ra liên tiếp) sau đó sẽ tạo ra kết quả sau từ đó cho các dữ liệu đưa ra:

   3 2
   1 5
   1 7

Điều này sau đó được phân tích cú pháp bởi awktập lệnh thứ hai diễn giải mỗi dòng là "Số dòng X có ký tự Y" và tạo ra đầu ra mong muốn.


Giải pháp thay thế là làm tất cả trong awkvà giữ số lượng độ dài trong một mảng. Đó là sự đánh đổi giữa hiệu quả, dễ đọc / dễ hiểu (và do đó có thể duy trì) giải pháp nào là "tốt nhất".

Giải pháp thay thế:

$ 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

Không cần sắp xếp trong awk (mảng được lập chỉ mục số được sắp xếp theo mặc định) (nhanh hơn).
Isaac

@Arrow Tôi biết. Tôi có giải pháp đó nhận xét trong câu trả lời của tôi vì Sundeep đã đánh bại tôi với nó trong vài giây. Tôi cũng ám chỉ điều này với đoạn cuối cùng của tôi.
Kusalananda

Tôi tin rằng nhận xét này sẽ hữu ích cho người dùng các giải pháp (không bao gồm trong câu trả lời của bạn (hoặc của Sundeep) :-) Nott). Mặt khác: bao gồm một nhận xét cho cùng một hiệu ứng trong câu trả lời của bạn và tôi vui vẻ sẽ xóa nhận xét của tôi. :-)
Isaac

10

Một cách khác để làm tất cả với awkmột mình

$ 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()]++ sử dụng độ dài của dòng đầu vào làm khóa để tiết kiệm số lượng
  • END{for(k in words)print k " character words - " words[k]} sau khi tất cả các dòng được xử lý, in nội dung của mảng ở định dạng mong muốn


So sánh hiệu suất, số được chọn là tốt nhất trong hai lần chạy

$ 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

Nếu tệp chỉ có các ký tự 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

Không chắc tại sao thời gian perlkhông thay đổi nhiều, có lẽ phải mã hóa theo cách khác


Tôi chỉ thêm nó vào giải pháp của riêng tôi. Đã xóa nó khi tôi nhìn thấy mặc dù của bạn. :-)
Kusalananda

yeah Tôi đã tranh luận để xóa của tôi trước khi thấy chỉnh sửa của bạn một lần nữa :)
Sundeep

Không cần phải sắp xếp một mảng được lập chỉ mục số . Đó là allways được đặt hàng với một chỉ số ngày càng tăng. (tốt, ít nhất là trong awk :-))
Isaac

lengthkhông có ()hoạt động hoàn toàn tốt ở đây, vì vậy có thể là dư thừa để thêm niềng răng. Tôi đang sử dụng GNU awk.
Sergiy Kolodyazhnyy

2
@SergiyKolodyazhnyy yup, hướng dẫn sử dụng gnu awk nóiIn 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

Đây là một perltương đương (với - tùy chọn - sắp xếp):

$ 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

Nếu các chỉ mục khóa là số: Mảng khóa có cần được sắp xếp trong Perl không?
Isaac

1
@Arrow: Câu trả lời này đang sử dụng hàm băm (tức là mảng kết hợp với các khóa chuỗi) và những câu trả lời có thứ tự khóa không xác định, vì vậy, có. Trên thực tế, câu trả lời hơi có lỗi vì nó sắp xếp các khóa dưới dạng chuỗi chứ không phải số. Thêm {$a<=>$b}sau sortsẽ khắc phục điều đó. Ngoài ra, người ta có thể sử dụng một mảng bình thường với các khóa số và chỉ cần bỏ qua bất kỳ khóa nào có giá trị bằng 0 / không xác định.
Ilmari Karonen

@IlmariKaronen Cảm ơn, tốt hơn bây giờ. Thật là một sự khác biệt niềng răng làm cho !!
Isaac

Sẽ hiệu quả hơn khi sử dụng một mảng thay vì băm. OP muốn hàng triệu dòng, vì vậy mọi chi phí kiểm tra và bỏ qua các số không trong khi in dễ dàng được bù lại bằng cách lập chỉ mục rẻ hơn.
Peter Cordes

5

Một thay thế một cuộc gọi đến GNU awk, sử dụng 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

Thuật toán cốt lõi chỉ thu thập số lượng ký tự trong một mảng. Phần cuối in số lượng thu thập được định dạng bằng printf.

Nhanh chóng, đơn giản, một cuộc gọi duy nhất để awk.

Nói chính xác: một số bộ nhớ nữa được sử dụng để giữ mảng.
Nhưng không có loại nào được gọi (chỉ mục mảng số được đặt luôn luôn được sắp xếp theo chiều ngang với PROCINFO) và chỉ có một chương trình bên ngoài : awk, thay vì một số.


1
for incó thể xảy ra để cung cấp các chỉ mục mảng số theo thứ tự số ít nhất cho một số giá trị hoặc trong một số triển khai awk, nhưng điều đó là không bắt buộc, không truyền thống và chắc chắn không phổ biến. Nó thường xảy ra đối với các bộ nhỏ như 2 hoặc 3 hoặc có thể 4; thử 10 hoặc 20 trên mỗi awk bạn có quyền truy cập (không có PROCINFO hoặc WHINY_USERS trong gawk) và tôi đặt cược $ 50 ít nhất một trường hợp không được sắp xếp.
dave_thndry_085

Cảm ơn vì đầu vào của bạn. Sử dụng này : Tôi tin rằng nó được sắp xếp ngay bây giờ. :-)
Isaac

1
@ind_str_ascsắp xếp dưới dạng chuỗi, sẽ chỉ đúng cho các số nếu tất cả chúng đều là một chữ số (như ví dụ của bạn); sử dụng @ind_num_ascnếu (bất kỳ) giá trị có thể là 10 hoặc nhiều hơn. Và mặc dù bây giờ nó không còn là vấn đề nữa so với trước đây, tính năng này chỉ được nâng cấp lên 4.0 .
dave_thndry_085
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.