วิธีการนับการเกิดขึ้นของตัวละครแต่ละตัว?


13

ตัวอย่างเช่นฉันมีไฟล์1.txtที่ประกอบด้วย:

Moscow
Astana
Tokyo
Ottawa

ฉันต้องการนับจำนวนถ่านทั้งหมดเป็น:

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

4
จากคำตอบที่ยอมรับมันไม่ชัดเจนอย่างสมบูรณ์คุณต้องการ "A" และ "a" ที่แตกต่างหรือไม่? คำถามของคุณแนะนำให้คุณทำ
Jacob Vlijm

คำตอบ:


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นับจำนวนที่เกิดขึ้น -iธงชาติuniqสามารถ ommited ถ้าคุณไม่ต้องการให้กรณีไม่รู้สึก


3
อันนี้ยอดเยี่ยม ข้อแม้พิเศษคือไปป์เอาท์พุทอีกครั้งในsort -k 2รายการตัวอักษรและตัวเลข
tetris11

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

ช้าไปหน่อย แต่ถ้าจะให้ครบชุดอีกอันหนึ่งไพ ธ อน (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 S 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

สิ่งนี้จะนับอักขระ 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,

หากคุณไม่ต้องการเพิกเฉยกรณีและปัญหาให้ลบออกiจาก grep (ในคำถามของคุณคุณมีเพียง 3 ข้อในผลลัพธ์ที่คาดไว้)
stalet

โอ้ขอบคุณ. "{a..z}" - เป็นสัญลักษณ์ทั้งหมดตั้งแต่ 'a' ถึง 'z' หรือไม่ สิ่งที่เกี่ยวกับสัญลักษณ์ที่พิมพ์ได้ทั้งหมดวิธีที่เรากำหนดได้โดยไม่ต้องแสดงรายการทั้งหมด
Set-xx

ฉันได้อัพเดทคำตอบของฉันพร้อมตัวอย่างเกี่ยวกับวิธีขยายการค้นหาสำหรับตัวละครที่อ่านได้ทั้งหมด
stalet

นั่นคือการโทรจำนวนมากไปgrepยังอินพุตทั้งหมดซ้ำ ๆ
200_success

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
  • มันสร้างอาร์เรย์เชื่อมโยงกับตัวละครแต่ละตัวเป็นค่าดัชนีและนับเป็นค่าอาร์เรย์
  • การกระทำ END พิมพ์อาร์เรย์

ไม่จำเป็นต้องcat file | awk '...': awk '...' fileคุณโดยตรงสามารถพูดได้
fedorqui

2

perloneliner ต่อไปนี้จะนับ ฉันใส่ regex ในบริบทรายการ (เพื่อให้ได้จำนวนการแข่งขัน) และใส่ลงในบริบท scalar:

$ 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

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ระดับการนับจำนวนการเกิดขึ้นของตัวละครแต่ละตัวแล้วสำหรับการพิมพ์วัตถุประสงค์ที่เราได้ใช้โมดูลจะได้รับทั้งหมดอักษรตัวพิมพ์เล็กโดยตัวแปรstringstring.lowercase

บันทึกสคริปต์ข้างในแฟ้มให้มันชื่อใด ๆ count.pyที่คุณต้องการเช่น ขณะนี้จากไดเรกทอรีเดียวกันที่ไฟล์จะถูกบันทึกไว้คุณก็สามารถเรียกใช้python count.pyในการดำเนินการแฟ้มจากการใช้ไดเรกทอรีอื่น ๆ python /absolute/path/to/count.pyเส้นทางสัมบูรณ์ไปยังไฟล์ที่จะดำเนินการได้คือ


คุณช่วยอธิบายวิธีแก้ปัญหาของคุณให้กระจ่างขึ้นได้ไหม ฉันหมายถึง: สร้างไฟล์ file_name ใส่รหัสนี้ chmod + x ฯลฯ และอื่น ๆ
c0rp

@ 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 ของตัวละครที่แตกต่างกันและใช้วิธีการนับในแต่ละคน

#!/usr/bin/env ruby

String content = IO.read("1.txt")
content.split("").uniq.sort.each { |chr| puts( chr + ' - ' + content.count(chr).to_s) }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.