Как подсчитать вхождения каждого персонажа?

Например, у меня есть файл 1.txt, который содержит:

Moscow
Astana
Tokyo
Ottawa

Я хочу подсчитать число всех символов как:

a - 4,
b - 0,
c - 1,
...
z - 0
1
задан 7 March 2015 в 02:13

10 ответов

Немного поздно, но для завершения набора, другого подхода python (3), отсортированного результата:

#!/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

Объяснение

Прочитайте файл, пропустите пробелы и вернитесь как «characters»:
chars = open(sys.argv[1]).read().strip().replace("\n", "")
Создать (отсортированный) набор uniques:
sorted(set([c for c in chars]))
Считать и распечатать запись для каждого из символов:
print(c+" -", chars.count(c)) for c in <uniques>

Как использовать

Прочитайте файл, пропустите пробелы и верните его как «символы»:
chars = open(sys.argv[1]).read().strip().replace("\n", "")

Прочитайте файл, пропустите пробелы и верните его как «символы»:

/path/to/chars_count.py </path/to/file>
, если скрипт исполняемый файл или:
python3 /path/to/chars_count.py </path/to/file>
, если это не
4
ответ дан 23 May 2018 в 22:40

По умолчанию в awk полевой разделитель (FS) представляет собой пробел или вкладку. Поскольку мы хотим подсчитать каждый символ, нам нужно будет переопределить FS ни к чему (FS=""), чтобы разделить каждый символ в отдельной строке и сохранить его в массив, а в конце внутри блока END{..} напечатать их общие вхождения на следующая команда awk:

$ 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

В блоке {for (i=1;i<=NF;i++) a[$i]++} ... FS="" ... мы просто разбиваем символы. И в блоке END{for (c in a) print c,a[c]} мы перебираем массив a и печатаем сохраненный символ в нем print c и его количество вхождений a[c]

4
ответ дан 23 May 2018 в 22:40

Следующий счетчик perl oneliner будет выполнять подсчет. Я поместил регулярное выражение в контекст списка (чтобы получить количество совпадений) и поместил его в скалярный контекст:

$ 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,
2
ответ дан 23 May 2018 в 22:40
  • 1
    Чтобы избавиться от конечной запятой, кажется, требуется значительная переписывание: perl -Mfeature=say -e '$a=join("",<>);say join(",\n", map { sprintf("%s - %d", $_, ($d=()=$a=~/$_/gi)); } ("a".."z"))' – 200_success 7 March 2015 в 02:45

Проведите цикл for для всех символов, которые хотите подсчитать, и используйте grep -io, чтобы получить все вхождения символа и игнорировать регистр, а wc -l - подсчитать экземпляры и распечатать результат. [!d0 ]

Нравится это:

#!/bin/bash

filename="1.txt"

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

Сценарий выводит это:

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 после комментария

Чтобы создать цикл для всех печатных символов вы можете сделать это:

#!/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

Это будет считать все символы ANSI от 32 до 126 - это наиболее читаемые. Обратите внимание, что это не использует случай игнорирования.

вывод из этого будет:

- 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,
2
ответ дан 23 May 2018 в 22:40
  • 1
    Если вы не хотите игнорировать регистр, удалите i из grep. (в вашем вопросе у вас было всего 3 результата) – stalet 6 March 2015 в 12:33
  • 2
    ой ну спасибо. & Quot; {a..z} & Quot; - все символы от 'a' до 'z'? о всех печатаемых символах, о том, как мы можем обозначить их, не перечисляя их всех – Set-xx 6 March 2015 в 14:58
  • 3
    Ive обновил мой ответ на примере того, как расширить поиск для всех читаемых символов – stalet 6 March 2015 в 18:48
  • 4
    Это много вызовов grep для всего ввода. – 200_success 7 March 2015 в 02:08

Вот решение, использующее 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]) + ','

Здесь мы использовали класс collections модуля Counter модуля для подсчета количества вхождений каждого символа, а затем для целей печати мы использовали модуль string, чтобы получить все буквы нижнего регистра переменной string.lowercase.

Сохраните вышеприведенный скрипт в файле, давая ему любое имя, которое вы хотите, например [F6]. Теперь из того же каталога, в котором сохранен файл, вы можете просто запустить python count.py для выполнения файла, из любого другого каталога используйте абсолютный путь к файлу для его выполнения, например, python /absolute/path/to/count.py.

2
ответ дан 23 May 2018 в 22:40
  • 1
    Не могли бы вы прояснить свое решение. Я имею в виду: создать файл file_name, поместить этот код, chmod + x и т. Д. И т. Д. И т. Д. – c0rp 6 March 2015 в 17:23
  • 2
    @ c0rp: сделано .... – heemayl 6 March 2015 в 20:48

Здесь другое решение (в 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
Создает ассоциативный массив с каждым символом в качестве значения индекса, а счетчик - значением массива. Действие END печатает массив.
2
ответ дан 23 May 2018 в 22:40
  • 1
    нет необходимости в cat file | awk '...': вы можете прямо сказать awk '...' file. – fedorqui 7 March 2015 в 02:22

Некоторое время назад я написал программу на C, чтобы сделать это, потому что мне нужно было посмотреть большие файлы и создать некоторую статистику.

#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;
}

скомпилировать с (если исходный код находится в character-distribution.c):

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

работает с:

./character-distribution < 1.txt

Если у вас нет компилятора C, установите GCC:

sudo apt-get install gcc build-essential
1
ответ дан 23 May 2018 в 22:40

Аналогичное решение для @heemayl, с более строгим кодом, которое работает на Python 2.7 и 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))

Первое утверждение, count = collections.Counter(…) выполняет всю реальную работу.

fileinput.input() читает каждую строку ввода, которая может быть передана через stdin или в качестве аргументов командной строки. * заставляет его рассматривать персонажа одновременно, а не линию за раз. count = Counter(…) учитывает вхождения каждого символа эффективно за один проход и сохраняет результат в переменной count.

Вторая строка просто печатает результаты.

fileinput.input() читает каждую строку ввода, которая может быть передана через stdin или в качестве аргументов командной строки. print(',\n'.join(…)) помещает его в желаемый формат: по одному на строку, разделен запятыми, но нет запятой в последней строке.
0
ответ дан 23 May 2018 в 22:40

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

Если у вас есть более ранняя версия GNU awk, вы можете использовать for (c in b) print c, b[c].

0
ответ дан 23 May 2018 в 22:40

Вот ответ, использующий ruby. Это делается путем изменения строки в список uniq разных символов и использование метода count для каждого из них.

#!/usr/bin/env ruby

String content = IO.read("1.txt")
content.split("").uniq.sort.each { |chr| puts( chr + ' - ' + content.count(chr).to_s) }
0
ответ дан 23 May 2018 в 22:40

Другие вопросы по тегам:

Похожие вопросы: