Come contare le occorrenze di ciascun personaggio?


13

Ad esempio ho un file 1.txtche contiene:

Moscow
Astana
Tokyo
Ottawa

Voglio contare il numero di tutti i caratteri come:

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

4
Dalla risposta accettata, non è del tutto chiaro, vuoi "A" e "a" distinti o no? la tua domanda ti suggerisce di farlo.
Jacob Vlijm,

Risposte:


20

Puoi usare questo:

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

La sedparte inserisce una nuova riga dopo ogni personaggio. Poi abbiamo sortl'uscita in ordine alfabetico. E infine uniqconta il numero di occorrenze. La -ibandiera di uniqpuò essere attivata se non si desidera l'insensibilità al caso.


3
È brillante. Un avvertimento in più sarebbe quello di reindirizzare l'output in sort -k 2per elencarli alfanumericamente.
tetris11,

3
Questo è il modo più breve, più comprensibile ma purtroppo il più lento
c0rp

In Mac OS XI ha dovuto all'uso sed -e $'s/\(.\)/\\1\\\n/g'(vedi anche stackoverflow.com/a/18410122/179014 )
asmaier

Per ordinare per il numero di occorrenze (discendente): | sort -rnk 1. E se hai a che fare con file molto grandi, come me, puoi semplicemente campionare alcune migliaia di righe per ottenere un proxy per i conteggi effettivi:cat 1.txt | shuf -n 10000 | sed 's/\(.\)/\1\n/g' | sort | uniq -ic | sort -rnk 1
cpury

6

Un po 'in ritardo, ma per completare il set, un altro approccio a Python (3), ha ordinato il risultato:

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

Spiegazione

  1. Leggi il file, salta gli spazi e ritorna come "caratteri":

    chars = open(sys.argv[1]).read().strip().replace("\n", "")
  2. Crea un set (ordinato) di elementi unici:

    sorted(set([c for c in chars]))
  3. Contare e stampare l'occorrenza per ciascuno dei personaggi:

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

Come usare

  1. Incolla il codice in un file vuoto, salvalo come chars_count.py
  2. Eseguilo con il file come argomento tramite:

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

    se lo script è eseguibile o:

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

    se non lo è


5

Per default in la F ield S eparator (FS) è spazio o tab . Poiché vogliamo contare ogni carattere, dovremo ridefinire il valore FS in nulla ( FS="") per dividere ogni carattere in una riga separata e salvarlo in un array e alla fine all'interno del END{..}blocco, stampare le loro occorrenze totali con il seguente comando :

$ 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

Nel {for (i=1;i<=NF;i++) a[$i]++} ... FS="" ...blocco abbiamo appena diviso i personaggi. E
in END{for (c in a) print c,a[c]}blocco stiamo eseguendo il looping per la matrice ae stampando il carattere salvato in esso print ce il suo numero di occorrenzea[c]


3

Fai un forciclo per tutti i personaggi che vuoi contare e usa grep -ioper ottenere tutte le occorrenze del personaggio e ignorare il caso, wc -lcontare le istanze e stampare il risultato.

Come questo:

#!/bin/bash

filename="1.txt"

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

Lo script genera questo:

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,

MODIFICA dopo il commento

Per creare un ciclo per tutti i caratteri stampabili è possibile effettuare ciò:

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

Questo conterà tutti i caratteri ANSI da 32 a 126 - questi sono i più comunemente leggibili. Si noti che questo non usa ignora maiuscole / minuscole.

l'output da questo sarà:

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

Se non vuoi ignorare il caso, rimuovi il ida grep. (nella tua domanda ne avevi solo 3 nel risultato atteso)
stalet il

Oh, grazie. "{a..z}" - sono tutti simboli dalla "a" alla "z"? che dire di tutti i simboli stampabili, come possiamo designarli senza elencarli tutti
Set-xx

Ho aggiornato la mia risposta con un esempio su come estendere la ricerca di tutti i personaggi leggibili
stalet

Sono molte le chiamate grepripetute all'intero input.
200_successo

3

Ecco un'altra soluzione (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
  • Crea una matrice associativa con ogni carattere come valore di indice e il conteggio come valore di matrice.
  • L'azione END stampa l'array.

non c'è bisogno cat file | awk '...': puoi dirlo direttamente awk '...' file.
fedorqui,

2

Il seguente perloneliner farà il conteggio. Inserisco la regex nel contesto dell'elenco (per ottenere il numero di corrispondenze) e lo inserisco nel contesto scalare:

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

Per sbarazzarsi della virgola finale sembra richiedere una riscrittura significativa:perl -Mfeature=say -e '$a=join("",<>);say join(",\n", map { sprintf("%s - %d", $_, ($d=()=$a=~/$_/gi)); } ("a".."z"))'
200_successo

2

Ecco una soluzione che utilizza 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]) + ','

Qui abbiamo usato la classe collectionsdel modulo Counterper contare il numero di occorrenze di ciascun carattere, quindi ai fini della stampa abbiamo usato il stringmodulo per ottenere tutte le lettere minuscole dalla variabile string.lowercase.

Salvare lo script sopra in un file dandogli il nome desiderato ad es count.py. Ora dalla stessa directory in cui viene salvato il file, è possibile eseguire semplicemente python count.pyper eseguire il file, da qualsiasi altra directory utilizzare il percorso assoluto del file per eseguirlo, ad es python /absolute/path/to/count.py.


Potresti chiarire la tua soluzione, per favore. Voglio dire: crea il file nome_file, inserisci questo codice, chmod + x ecc. Ecc. Ecc.
c0rp

@ c0rp: done ....
heemayl

1

Qualche tempo fa ho scritto un programma C per farlo, perché ne avevo bisogno per guardare file di grandi dimensioni e produrre alcune statistiche.

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

compilare con (supponendo che il codice sorgente risieda in character-distribution.c):

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

Corri con:

./character-distribution < 1.txt

Se non hai un compilatore C pronto, installa GCC:

sudo apt-get install gcc build-essential

0

Soluzione simile a @heemayl, con codice più stretto, che funziona su Python 2.7 e 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))

La prima affermazione, count = collections.Counter(…)fa tutto il vero lavoro.

  • fileinput.input() legge ogni riga dell'input, che può essere reindirizzata tramite stdin o come argomenti della riga di comando.
  • * lo fa considerare un personaggio alla volta piuttosto che una linea alla volta.
  • count = Counter(…)conta le occorrenze di ciascun personaggio in modo efficiente, in un unico passaggio e memorizza il risultato nella countvariabile.

La seconda riga stampa solo i risultati.

  • '{} - {}'.format(c, count[c] + count[c.upper()]) for c in string.ascii_lowercase fa un elenco di ogni personaggio e il suo conteggio.
  • print(',\n'.join(…)) lo mette nel formato desiderato: uno per riga, separato da virgole, ma nessuna virgola sull'ultima riga.

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

Se hai una versione precedente di GNU awk puoi usarla for (c in b) print c, b[c].


0

Ecco la risposta usando ruby. Viene fatto cambiando la stringa in un elenco uniq dei diversi caratteri e usando il metodo count su ciascuno di essi.

#!/usr/bin/env ruby

String content = IO.read("1.txt")
content.split("").uniq.sort.each { |chr| puts( chr + ' - ' + content.count(chr).to_s) }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.