Làm thế nào để đếm sự xuất hiện của mỗi nhân vật?


13

Ví dụ tôi có tệp 1.txt, có chứa:

Moscow
Astana
Tokyo
Ottawa

Tôi muốn đếm số lượng của tất cả các char là:

a - 4,
b - 0,
c - 1,
...
z - 0

4
Từ câu trả lời được chấp nhận, nó không hoàn toàn rõ ràng, bạn có muốn phân biệt "A" và "a" hay không? Câu hỏi của bạn cho thấy bạn làm.
Jacob Vlijm 6/03/2015

Câu trả lời:


20

Bạn có thể sử dụng điều này:

sed 's/\(.\)/\1\n/g' 1.txt | sort | uniq -ic
  4  
  5 a
  1 c
  1 k
  1 M
  1 n
  5 o
  2 s
  4 t
  2 w
  1 y

Phần sedđặt một dòng mới sau mỗi nhân vật. Sau đó, chúng tôi sortouput theo thứ tự abc. Và cuối cùng uniqđếm số lần xuất hiện. Các -ilá cờ của uniqthể Bỏ qua phần nếu bạn không muốn trường hợp vô hồn.


3
Điều này thật tuyệt vời. Một cảnh báo bổ sung sẽ là đưa đầu ra một lần nữa vào sort -k 2danh sách chữ và số.
tetris11

3
Đây là con đường ngắn nhất, dễ hiểu nhất nhưng may mắn là chậm nhất
c0rp

Trên Mac OS XI đã phải sử dụng sed -e $'s/\(.\)/\\1\\\n/g'(xem thêm stackoverflow.com/a/18410122/179014 )
asmaier

Để đặt hàng theo số lần xuất hiện (giảm dần) : | sort -rnk 1. Và nếu bạn đang xử lý các tệp rất lớn, như tôi, bạn có thể lấy mẫu vài nghìn dòng để lấy proxy cho số lượng thực tế:cat 1.txt | shuf -n 10000 | sed 's/\(.\)/\1\n/g' | sort | uniq -ic | sort -rnk 1
cpury

6

Hơi muộn một chút, nhưng để hoàn thành bộ, một cách tiếp cận python (3) khác, đã sắp xếp kết quả:

#!/usr/bin/env python3
import sys

chars = open(sys.argv[1]).read().strip().replace("\n", "")
[print(c+" -", chars.count(c)) for c in sorted(set([c for c in chars]))]

A - 1
M - 1
O - 1
T - 1
a - 4
c - 1
k - 1
n - 1
o - 4
s - 2
t - 3
w - 2
y - 1

Giải trình

  1. Đọc tệp, bỏ qua khoảng trắng và trả về dưới dạng "ký tự":

    chars = open(sys.argv[1]).read().strip().replace("\n", "")
  2. Tạo một tập hợp (sắp xếp) các đơn vị:

    sorted(set([c for c in chars]))
  3. Đếm và in sự xuất hiện của từng ký tự:

    print(c+" -", chars.count(c)) for c in <uniques>

Cách sử dụng

  1. Dán mã vào một tệp trống, lưu nó dưới dạng chars_count.py
  2. Chạy nó với tệp dưới dạng đối số:

    /path/to/chars_count.py </path/to/file>

    nếu tập lệnh có thể thực thi được, hoặc:

    python3 /path/to/chars_count.py </path/to/file>

    nếu nó không phải là


5

Theo mặc định trong , eparator F ield S (FS) là khoảng trắng hoặc tab . Vì chúng tôi muốn đếm từng ký tự, chúng tôi sẽ phải xác định lại FS thành không có gì ( FS="") để tách từng ký tự thành một dòng riêng biệt và lưu nó thành một mảng và ở cuối END{..}khối bên trong , in tổng số lần xuất hiện của chúng bằng lệnh sau :

$ awk '{for (i=1;i<=NF;i++) a[$i]++} END{for (c in a) print c,a[c]}' FS="" file
A 1
M 1
O 1
T 1
a 4
c 1
k 1
n 1
o 4
s 2
t 3
w 2
y 1

Trong {for (i=1;i<=NF;i++) a[$i]++} ... FS="" ...khối chúng tôi chỉ chia các ký tự. Và
trong END{for (c in a) print c,a[c]}khối, chúng tôi đang lặp lại mảng avà in ký tự đã lưu trong đó print cvà số lần xuất hiện của nóa[c]


3

Thực hiện một forvòng lặp cho tất cả các ký tự bạn muốn đếm và sử dụng grep -iođể có được tất cả các lần xuất hiện của ký tự và trường hợp bỏ qua, và wc -lđể đếm các trường hợp và in kết quả.

Như thế này:

#!/bin/bash

filename="1.txt"

for char in {a..z}
do
    echo "${char} - `grep -io "${char}" ${filename} | wc -l`,"
done

Kịch bản đầu ra này:

a - 5,
b - 0,
c - 1,
d - 0,
e - 0,
f - 0,
g - 0,
h - 0,
i - 0,
j - 0,
k - 1,
l - 0,
m - 1,
n - 1,
o - 5,
p - 0,
q - 0,
r - 0,
s - 2,
t - 4,
u - 0,
v - 0,
w - 2,
x - 0,
y - 1,
z - 0,

EDIT sau khi bình luận

Để tạo một vòng lặp cho tất cả các ký tự có thể in, bạn có thể làm điều này:

#!/bin/bash

filename="a.txt"

for num in {32..126}
do
   char=`printf "\x$(printf %x ${num})"`
   echo "${char} - `grep -Fo "${char}" ${filename} | wc -l`,"
done

Điều này sẽ đếm tất cả các ký tự ANSI từ 32 đến 126 - đây là những ký tự phổ biến nhất có thể đọc được. Lưu ý rằng điều này không sử dụng trường hợp bỏ qua.

đầu ra từ đây sẽ là:

- 0,
! - 0,
" - 0,
# - 0,
$ - 0,
% - 0,
& - 0,
' - 0,
( - 0,
) - 0,
* - 0,
+ - 0,
, - 0,
- - 0,
. - 0,
/ - 0,
0 - 0,
1 - 0,
2 - 0,
3 - 0,
4 - 0,
5 - 0,
6 - 0,
7 - 0,
8 - 0,
9 - 0,
: - 0,
; - 0,
< - 0,
= - 0,
> - 0,
? - 0,
@ - 0,
A - 1,
B - 0,
C - 0,
D - 0,
E - 0,
F - 0,
G - 0,
H - 0,
I - 0,
J - 0,
K - 0,
L - 0,
M - 1,
N - 0,
O - 1,
P - 0,
Q - 0,
R - 0,
S - 0,
T - 1,
U - 0,
V - 0,
W - 0,
X - 0,
Y - 0,
Z - 0,
[ - 0,
\ - 0,
] - 0,
^ - 0,
_ - 0,
` - 0,
a - 4,
b - 0,
c - 1,
d - 0,
e - 0,
f - 0,
g - 0,
h - 0,
i - 0,
j - 0,
k - 1,
l - 0,
m - 0,
n - 1,
o - 4,
p - 0,
q - 0,
r - 0,
s - 2,
t - 3,
u - 0,
v - 0,
w - 2,
x - 0,
y - 1,
z - 0,
{ - 0,
| - 0,
} - 0,
~ - 0,

Nếu bạn không muốn bỏ qua trường hợp sau đó loại bỏ ikhỏi grep. (trong câu hỏi của bạn, bạn chỉ có 3 trong kết quả mong đợi)
stalet

ồ cảm ơn. "{A..z}" - tất cả các ký hiệu từ 'a' đến 'z'? những gì về tất cả các biểu tượng có thể in được, làm thế nào chúng ta có thể chỉ định chúng mà không liệt kê tất cả chúng
Set-xx

Ive đã cập nhật câu trả lời của tôi với một ví dụ về cách mở rộng tìm kiếm cho tất cả các nhân vật có thể đọc được
stalet 6/03/2015

Đó là rất nhiều cuộc gọi đến greptoàn bộ đầu vào liên tục.
200_success 6/03/2015

3

Đây là một giải pháp khác (trong awk) ...

awk '
        { for (indx=length($0); indx >= 1; --indx)
                ++chars[tolower(substr($0, indx, 1))]
        }
END     { for (c in chars) print c, chars[c]; }
' 1.txt | sort
  • Nó tạo ra một mảng kết hợp với mỗi ký tự là giá trị chỉ mục và được tính là giá trị mảng.
  • Hành động END in mảng.

Không cần phải cat file | awk '...': bạn có thể trực tiếp nói awk '...' file.
fedorqui

2

Các perloneliner sau đây sẽ làm số đếm. Tôi đặt regex trong ngữ cảnh danh sách (để lấy số lượng các trận đấu) và đặt nó vào bối cảnh vô hướng:

$ perl -e '$a=join("",<>);for("a".."z"){$d=()=$a=~/$_/gi;print"$_ - $d,\n"}' 1.txt
a - 5,
b - 0,
c - 1,
d - 0,
e - 0,
f - 0,
g - 0,
h - 0,
i - 0,
j - 0,
k - 1,
l - 0,
m - 1,
n - 1,
o - 5,
p - 0,
q - 0,
r - 0,
s - 2,
t - 4,
u - 0,
v - 0,
w - 2,
x - 0,
y - 1,
z - 0,

Để thoát khỏi dấu phẩy dường như cần phải viết lại đáng kể:perl -Mfeature=say -e '$a=join("",<>);say join(",\n", map { sprintf("%s - %d", $_, ($d=()=$a=~/$_/gi)); } ("a".."z"))'
200_success

2

Đây là một giải pháp sử dụng Python:

#!/usr/bin/env python2
import collections, string
with open('1.txt') as f:
    input_string = f.read().replace('\n', '').lower()
    count_dict = collections.Counter(input_string)
    for char in string.lowercase:
        print char + ' - ' + str(count_dict[char]) + ','

Ở đây, chúng tôi đã sử dụng lớp collectionscủa mô-đun Counterđể đếm số lần xuất hiện của mỗi ký tự, sau đó để in, chúng tôi đã sử dụng stringmô-đun để lấy tất cả các chữ cái viết thường theo biến string.lowercase.

Lưu tập lệnh trên trong một tệp đặt cho nó bất kỳ tên nào bạn muốn, vd count.py. Bây giờ từ cùng một thư mục lưu tệp, bạn có thể chỉ cần chạy python count.pyđể thực thi tệp, từ bất kỳ thư mục nào khác sử dụng đường dẫn tuyệt đối đến tệp để thực thi nghĩa là python /absolute/path/to/count.py.


Bạn có thể vui lòng làm rõ giải pháp của bạn. Ý tôi là: tạo tệp file_name, đặt mã này, chmod + x, v.v.
c0rp

@ c0rp: xong ....
heemayl

1

Cách đây một thời gian, tôi đã viết một chương trình C để làm điều đó, bởi vì tôi cần nó để xem các tệp lớn và tạo ra một số thống kê.

#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <ctype.h>
#include <limits.h>
#include <math.h>
#include <sysexits.h>


inline static double square(double x)
{
    return x * x;
}


int main()
{
    static const unsigned distribution_size = 1 << CHAR_BIT;

    int rv = EX_OK;
    uintmax_t *distribution = calloc(distribution_size, sizeof(*distribution));

    {
        int c;
        while ((c = getchar()) != EOF)
            distribution[c]++;

        if (ferror(stdin)) {
            perror("I/O error on standard input");
            rv = EX_IOERR;
        }
    }

    uintmax_t sum = 0;
    for (unsigned i = 0; i != distribution_size; i++)
        sum += distribution[i];
    double avg = (double) sum / distribution_size;

    double var_accum = 0.0;
    for (unsigned i = 0; i != distribution_size; i++)
    {
        const uintmax_t x = distribution[i];

        printf("'%c' (%02X): %20ju", isprint((int) i) ? i : ' ', i, x);
        if (x != 0) {
            var_accum += square((double) x - avg);
            printf(" (%+.2e %%)\n", ((double) x / avg - 1.0) * 100.0);
        } else {
            var_accum += square(avg);
            putchar('\n');
        }
    }

    double stdev = sqrt(var_accum / distribution_size);
    double varcoeff = stdev / avg;
    printf(
        "total: %ju\n"
        "average: %e\n"
        "standard deviation: %e\n"
        "variation coefficient: %e\n",
        sum, avg, stdev, varcoeff);

    free(distribution);
    return rv;
}

biên dịch với (giả sử mã nguồn nằm trong character-distribution.c):

cc -std=c99 -O2 -g0 -o character-distribution character-distribution.c

chạy với:

./character-distribution < 1.txt

Nếu bạn chưa có trình biên dịch C, hãy cài đặt GCC:

sudo apt-get install gcc build-essential

0

Giải pháp tương tự với @heemayl, với mã chặt chẽ hơn, hoạt động trên Python 2.7 và Python 3.

#!/usr/bin/python

import collections
import fileinput
import itertools
import string

count = collections.Counter(itertools.chain(*fileinput.input()))
print(',\n'.join('{} - {}'.format(c, count[c] + count[c.upper()])
                 for c in string.ascii_lowercase))

Các tuyên bố đầu tiên, count = collections.Counter(…)làm tất cả các công việc thực sự.

  • fileinput.input() đọc mọi dòng của đầu vào, có thể được dẫn qua stdin hoặc dưới dạng đối số dòng lệnh.
  • * làm cho nó xem xét một nhân vật tại một thời điểm chứ không phải là một dòng tại một thời điểm.
  • count = Counter(…)đếm số lần xuất hiện của từng ký tự một cách hiệu quả, trong một lần chạy và lưu kết quả vào countbiến.

Dòng thứ hai chỉ in kết quả.

  • '{} - {}'.format(c, count[c] + count[c.upper()]) for c in string.ascii_lowercase làm cho một danh sách của mỗi nhân vật và số lượng của nó.
  • print(',\n'.join(…)) đặt nó ở định dạng mong muốn: một trên mỗi dòng, được phân tách bằng dấu phẩy, nhưng không có dấu phẩy trên dòng cuối cùng.

0

GNU awk 4.1

awk -iwalkarray '{for (;NF;NF--) b[$NF]++} END {walk_array(b)}' FS=
[A] = 1
[O] = 1
[w] = 2
[k] = 1
[y] = 1
[T] = 1
[n] = 1
[a] = 4
[o] = 4
[c] = 1
[s] = 2
[t] = 3
[M] = 1

Nếu bạn có phiên bản GNU awk trước đó, bạn có thể sử dụng for (c in b) print c, b[c].


0

Dưới đây là câu trả lời bằng ruby. Nó được thực hiện bằng cách thay đổi chuỗi thành một danh sách uniq của các ký tự khác nhau và sử dụng phương thức đếm trên mỗi ký tự.

#!/usr/bin/env ruby

String content = IO.read("1.txt")
content.split("").uniq.sort.each { |chr| puts( chr + ' - ' + content.count(chr).to_s) }
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.