Wie werden die Vorkommen der einzelnen Charaktere gezählt?

13

Zum Beispiel habe ich eine Datei 1.txt, die enthält:

Moscow
Astana
Tokyo
Ottawa

Ich möchte die Anzahl aller Zeichen zählen als:

a - 4,
b - 0,
c - 1,
...
z - 0
Set-xx
quelle
4
Aus der akzeptierten Antwort geht nicht ganz hervor, ob Sie "A" und "A" unterscheiden wollen oder nicht? Ihre Frage legt nahe, dass Sie dies tun.
Jacob Vlijm

Antworten:

20

Sie könnten dies verwenden:

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

Das sedTeil fügt nach jedem Zeichen eine neue Zeile ein. Dann geben wir sortdie Ausgabe in alphabetischer Reihenfolge ein. Und schließlich uniqzählt die Anzahl der Vorkommen. Die Markierung -ivon uniqkann weggelassen werden, wenn Sie keine Unterscheidung zwischen Groß- und Kleinschreibung wünschen.

Chaos
quelle
3
Das ist brilliant. Eine zusätzliche Einschränkung wäre, die Ausgabe erneut in sort -k 2eine alphanumerische Liste umzuleiten.
Tetris11
3
Dies ist der kürzeste Weg, die meisten verständlich , aber ufortunately die langsamste
c0rp
Unter Mac OS XI musste sed -e $'s/\(.\)/\\1\\\n/g'(siehe auch stackoverflow.com/a/18410122/179014 )
Asmaier
Zur Bestellung durch die Anzahl der Ereignisse (absteigend): | sort -rnk 1. Und wenn Sie mit sehr großen Dateien arbeiten, wie ich es bin, können Sie einfach ein paar Tausend Zeilen cat 1.txt | shuf -n 10000 | sed 's/\(.\)/\1\n/g' | sort | uniq -ic | sort -rnk 1
abtasten
6

Etwas spät, aber um das Set zu vervollständigen, ein anderer Python (3) -Ansatz, sortiertes Ergebnis:

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

Erläuterung

  1. Lesen Sie die Datei, überspringen Sie Leerzeichen und geben Sie sie als "Zeichen" zurück:

    chars = open(sys.argv[1]).read().strip().replace("\n", "")
  2. Erstellen Sie eine (sortierte) Menge von Unikaten:

    sorted(set([c for c in chars]))
  3. Zähle und drucke das Vorkommen für jedes der Zeichen:

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

Wie benutzt man

  1. Fügen Sie den Code in eine leere Datei ein und speichern Sie ihn unter chars_count.py
  2. Führen Sie es mit der Datei als Argument aus:

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

    wenn das Skript ausführbar ist, oder:

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

    wenn nicht

Jacob Vlijm
quelle
5

Standardmäßig in die F ield S eparator (FS) ist Raum oder Tab . Da wir jedes Zeichen zählen möchten, müssen wir den FS in nothing ( FS="") umdefinieren , um jedes Zeichen in einer separaten Zeile zu teilen und in einem Array zu speichern. Am Ende des END{..}Blocks geben wir dessen Gesamtvorkommen mit dem folgenden Befehl aus:

$ 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

Im {for (i=1;i<=NF;i++) a[$i]++} ... FS="" ...Block teilen wir nur die Zeichen. Und
im END{for (c in a) print c,a[c]}Block werden die agespeicherten Zeichen print cund ihre Häufigkeit in einer Schleife angeordnet und gedruckta[c]

αғsнιη
quelle
3

Führen Sie eine forSchleife für alle Zeichen durch, die Sie zählen möchten, und verwenden Sie diese grep -io, um alle Vorkommen des Zeichens abzurufen und Groß- und Kleinschreibung zu ignorierenwc -l Instanzen zu zählen und das Ergebnis auszudrucken.

So was:

#!/bin/bash

filename="1.txt"

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

Das Skript gibt Folgendes aus:

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,

BEARBEITEN nach Kommentar

So erstellen Sie eine Schleife für alle druckbaren Zeichen:

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

Dies zählt alle ANSI-Zeichen von 32 bis 126 - die am häufigsten lesbaren. Beachten Sie, dass dies keine Groß- und Kleinschreibung verwendet.

Ausgabe davon wird sein:

- 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,
Stalet
quelle
Wenn Sie die Groß- / Kleinschreibung nicht ignorieren möchten, entfernen Sie das iaus dem grep. (In Ihrer Frage hatten Sie nur 3 im erwarteten Ergebnis)
stalet
oh Danke. "{a..z}" - Alle Symbole von 'a' bis 'z'? was ist mit allen druckbaren Symbolen, wie wir sie bezeichnen können, ohne sie alle aufzulisten
Set-xx
Ich habe meine Antwort mit einem Beispiel über die Erweiterung der Suche nach allen lesbaren Zeichen
aktualisiert
Das ist eine Menge Anrufe auf grepden gesamten Eingang wiederholt.
200_success
3

Hier eine andere Lösung (in 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
  • Es wird ein assoziatives Array mit jedem Zeichen als Indexwert und der Anzahl als Arraywert erstellt.
  • Die Aktion END druckt das Array.
Howard H
quelle
keine Notwendigkeit cat file | awk '...': Sie können direkt sagen awk '...' file.
Fedorqui
2

Der folgende perlOneliner wird die Zählung durchführen. Ich habe den regulären Ausdruck in den Listenkontext gestellt (um die Anzahl der Übereinstimmungen zu erhalten) und das in den skalaren Kontext gestellt:

$ 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,
Sylvain Pineau
quelle
Um das nachgestellte Komma loszuwerden, ist anscheinend ein deutliches Umschreiben erforderlich:perl -Mfeature=say -e '$a=join("",<>);say join(",\n", map { sprintf("%s - %d", $_, ($d=()=$a=~/$_/gi)); } ("a".."z"))'
200_success
2

Hier ist eine Lösung mit 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]) + ','

Hier haben wir collectionsdie CounterKlasse des Moduls verwendet, um die Anzahl der Vorkommen jedes Zeichens zu zählen, und zum Drucken haben wir das stringModul verwendet, um alle Kleinbuchstaben der Variablen abzurufen string.lowercase.

Speichern Sie das obige Skript in einer Datei, und geben Sie ihm einen beliebigen Namen, z count.py. Von demselben Verzeichnis aus, in dem die Datei gespeichert ist, können Sie sie einfach python count.pyausführen. In jedem anderen Verzeichnis verwenden Sie den absoluten Pfad zur Datei, um sie auszuführen python /absolute/path/to/count.py.

heemayl
quelle
Könnten Sie bitte Ihre Lösung klären. Ich meine: Datei Dateiname erstellen, diesen Code setzen, chmod + x usw. usw. usw.
c0rp
@ c0rp: erledigt ....
heemayl
1

Vor einiger Zeit habe ich ein C-Programm dafür geschrieben, weil ich es brauchte, um große Dateien zu betrachten und Statik zu erzeugen.

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

kompiliere mit (vorausgesetzt der Quellcode befindet sich in character-distribution.c):

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

lauf mit:

./character-distribution < 1.txt

Wenn Sie keinen C-Compiler bereit haben, installieren Sie GCC:

sudo apt-get install gcc build-essential
David Foerster
quelle
0

Ähnliche Lösung zu @heemayl, mit engerem Code, der auf Python 2.7 und Python 3 funktioniert.

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

Die erste Aussage count = collections.Counter(…)erledigt die ganze wirkliche Arbeit.

  • fileinput.input() Liest jede Zeile der Eingabe, die über stdin oder als Befehlszeilenargumente weitergeleitet werden kann.
  • * Lässt es ein Zeichen auf einmal betrachten, anstatt eine Zeile auf einmal.
  • count = Counter(…)Zählt die Vorkommen jedes Zeichens effizient in einem Durchgang und speichert das Ergebnis in der countVariablen.

Die zweite Zeile gibt nur die Ergebnisse aus.

  • '{} - {}'.format(c, count[c] + count[c.upper()]) for c in string.ascii_lowercase erstellt eine Liste aller Zeichen und ihrer Anzahl.
  • print(',\n'.join(…)) Versetzt es in das gewünschte Format: eins pro Zeile, getrennt durch Kommas, aber kein Komma in der letzten Zeile.
200_erfolg
quelle
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

Wenn Sie eine frühere Version von GNU awk haben, können Sie diese verwenden for (c in b) print c, b[c].

Steven Penny
quelle
0

Hier ist die Antwort mit Rubin. Dazu wird die Zeichenfolge in eine eindeutige Liste der verschiedenen Zeichen geändert und für jedes Zeichen die Zählmethode verwendet.

#!/usr/bin/env ruby

String content = IO.read("1.txt")
content.split("").uniq.sort.each { |chr| puts( chr + ' - ' + content.count(chr).to_s) }
Stalet
quelle