如何计算每个字符的出现次数?


13

例如,我有file 1.txt,其中包含:

Moscow
Astana
Tokyo
Ottawa

我想将所有字符的数量计算为:

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

4
从接受的答案来看,还不是很清楚,您是否要区分“ A”和“ a”?您的问题建议您这样做。
Jacob Vlijm 2015年

Answers:


20

您可以使用此:

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

sed部分在每个字符后放置换行符。然后我们sort按字母顺序输出。最后uniq算出发生的次数。如果您不希望区分大小写-iuniq可以省略的标记。


3
太好了 一个额外的警告是将输出再次通过管道输入sort -k 2以字母顺序列出它们。
tetris11'3

3
这是最短的方法,最容易理解,但最慢的是
c0rp

在Mac OS XI不得不使用sed -e $'s/\(.\)/\\1\\\n/g'(也见stackoverflow.com/a/18410122/179014
asmaier

排序:按出现次数(降序)排序:| sort -rnk 1。如果您正在处理非常大的文件(例如我),则可以抽样几千行以获取实际计数的代理:cat 1.txt | shuf -n 10000 | sed 's/\(.\)/\1\n/g' | sort | uniq -ic | sort -rnk 1
cpury

6

有点晚了,但是为了完成设置,另一种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

说明

  1. 读取文件,跳过空格并以“字符”形式返回:

    chars = open(sys.argv[1]).read().strip().replace("\n", "")
  2. 创建一组(排序的)唯一性:

    sorted(set([c for c in chars]))
  3. 计算并打印每个字符的出现次数:

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

如何使用

  1. 将代码粘贴到一个空文件中,另存为 chars_count.py
  2. 通过以下任一方式将文件作为参数运行:

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

    如果脚本是可执行的,或者:

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

    如果不是


5

默认情况下,在˚F ield 小号 eparator(FS)是空间标签。由于我们要计算每个字符,因此我们必须将FS重新定义为nothing(FS=""),以将每个字符拆分为单独的行并将其保存到数组中,并在END{..}块的末尾,通过以下命令打印它们的总出现次数:命令:

$ 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]


3

for对要计数的所有字符进行循环,并使用它grep -io来获取该字符的所有wc -l出现次数并忽略大小写,并对实例进行计数,然后打印结果。

像这样:

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

评论后编辑

要为所有可打印字符创建循环,可以执行以下操作:

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

这将计算从32到126的所有ANSI字符-这些是最易读的字符。请注意,这不使用忽略大小写。

这样的输出将是:

- 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,

如果您不想忽略大小写,请i从grep中删除。(在您的问题中,预期结果只有3个)
stalet

哦谢谢。“ {a..z}”-从'a'到'z'都是符号吗?那么所有可打印符号呢,我们如何在不列出所有符号的情况下指定它们
Set-xx

香港专业教育学院用一个关于如何扩展所有可读字符的搜索示例更新了我的答案
stalet

这是一个很大调用的grep整个输入多次。
200_success 2015年

3

这是另一种解决方案(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
  • 它创建一个关联数组,其中每个字符作为索引值,而count作为数组值。
  • END操作将打印阵列。

无需cat file | awk '...':您可以直接说awk '...' file
fedorqui 2015年

2

下面的perloneliner将进行计数。我将正则表达式放在列表上下文中(以获取匹配数),并将其放入标量上下文中:

$ 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,

要摆脱尾随的逗号,似乎需要进行大量重写:perl -Mfeature=say -e '$a=join("",<>);say join(",\n", map { sprintf("%s - %d", $_, ($d=()=$a=~/$_/gi)); } ("a".."z"))'
200_success 2015年

2

这是使用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

将上述脚本保存在文件中,并为其指定所需的名称,例如count.py。现在,从保存文件的同一目录中,您可以简单地运行python count.py以执行文件,而从任何其他目录中,请使用文件的绝对路径来执行该文件,即python /absolute/path/to/count.py


您能否阐明您的解决方案。我的意思是:创建文件file_name,放入此代码,chmod + x等,等等。等等
c0rp 2015年

@ c0rp:做....
heemayl

1

前一阵子我写了一个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

0

与@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变量中。

第二行仅打印结果。

  • '{} - {}'.format(c, count[c] + count[c.upper()]) for c in string.ascii_lowercase 列出每个字符及其计数。
  • print(',\n'.join(…)) 将其设置为所需的格式:每行一个,以逗号分隔,但最后一行没有逗号。

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

如果您有早期版本的GNU awk,则可以使用for (c in b) print c, b[c]


0

这是使用红宝石的答案。通过将字符串更改为不同字符的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) }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.