Par exemple, j'ai le fichier 1.txt
, qui contient:
Moscow
Astana
Tokyo
Ottawa
Je veux compter le nombre de tous les caractères comme:
a - 4,
b - 0,
c - 1,
...
z - 0
Vous pouvez utiliser ceci:
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 partie sed
place une nouvelle ligne après chaque caractère. Ensuite, nous sort
la sortie par ordre alphabétique. Et enfin uniq
compte le nombre d’occurrences. L'indicateur -i
de uniq
peut être omis si vous ne voulez pas que la casse soit respectée.
Un peu tard, mais pour compléter l'ensemble, une autre approche python (3), résultat trié:
#!/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
Lisez le fichier, ignorez les espaces et retourne sous forme de "caractères":
chars = open(sys.argv[1]).read().strip().replace("\n", "")
Créez un ensemble (unique) de pièces uniques:
sorted(set([c for c in chars]))
Comptez et imprimez l'occurrence pour chacun des personnages:
print(c+" -", chars.count(c)) for c in <uniques>
chars_count.py
Exécutez-le avec le fichier comme argument de l'une des manières suivantes:
/path/to/chars_count.py </path/to/file>
si le script est exécutable, ou:
python3 /path/to/chars_count.py </path/to/file>
si ce n'est pas
Par défaut dans awk le champ FS eparator (FS) est espace ou tab. Puisque nous voulons compter chaque caractère, nous devrons redéfinir le FS à rien (FS=""
) pour scinder chaque caractère sur une ligne distincte et l'enregistrer dans un tableau et à la fin du bloc insideEND{..}
, afficher leur nombre total d'occurrences. par la commande awk suivante:
$ 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
Dans le bloc {for (i=1;i<=NF;i++) a[$i]++} ... FS="" ...
, nous divisons simplement les caractères. Et
dans le bloc END{for (c in a) print c,a[c]}
, nous bouclons sur le tableau a
et y imprimons le caractère enregistré print c
et le nombre d’occurrences a[c]
Voici une autre solution (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
Effectuez une boucle for
pour tous les caractères à compter et utilisez grep -io
pour obtenir toutes les occurrences du caractère et de la casse ignorée, et wc -l
pour compter les occurrences et imprimer le résultat.
Comme ça:
#!/bin/bash
filename="1.txt"
for char in {a..z}
do
echo "${char} - `grep -io "${char}" ${filename} | wc -l`,"
done
Le script affiche ceci:
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 after comment
Pour créer une boucle pour tous les caractères imprimables, procédez comme suit:
#!/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
Ceci comptera tous les caractères ANSI de 32 à 126 - ce sont les plus lisibles. Notez que cela n'utilise pas ignorer la casse.
la sortie de ceci sera:
- 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,
Voici une solution utilisant 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]) + ','
Ici, nous avons utilisé la classe collections
du module Counter
pour compter le nombre d'occurrences de chaque caractère, puis à des fins d'impression, nous avons utilisé le module string
pour obtenir toutes les lettres minuscules à l'aide de la variable string.lowercase
.
Enregistrez le script ci-dessus dans un fichier en lui donnant le nom de votre choix, par exemple. count.py
. Maintenant, à partir du même répertoire où le fichier est enregistré, vous pouvez simplement exécuter python count.py
pour exécuter le fichier. À partir de tout autre répertoire, utilisez le chemin absolu du fichier pour l’exécuter, c.-à-d. python /absolute/path/to/count.py
.
L’élément Perl
suivant fera le décompte. Je mets la regex dans le contexte de la liste (pour obtenir le nombre de correspondances) et le mets dans un contexte scalaire:
$ 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,
Il y a quelque temps, j’ai écrit un programme C pour le faire, car j’avais besoin de regarder les fichiers gros et de produire des statistiques.
#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;
}
compiler avec (en supposant que le code source réside dans character-distribution.c
):
cc -std=c99 -O2 -g0 -o character-distribution character-distribution.c
courir avec:
./character-distribution < 1.txt
Si vous n'avez pas de compilateur C prêt, installez GCC:
Sudo apt-get install gcc build-essential
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
Si vous avez une version antérieure de GNU awk, vous pouvez utiliser for (c in b) print c, b[c]
.
Voici la réponse en utilisant Ruby. Cela se fait en changeant la chaîne en une liste uniq des différents caractères et en utilisant la méthode count sur chacun d'eux.
#!/usr/bin/env Ruby
String content = IO.read("1.txt")
content.split("").uniq.sort.each { |chr| puts( chr + ' - ' + content.count(chr).to_s) }
Solution similaire à @heemayl, avec un code plus strict, qui fonctionne sur Python 2.7 et 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 première instruction, count = collections.Counter(…)
, effectue tout le travail réel.
fileinput.input()
lit chaque ligne de l'entrée, qui peut être acheminée via stdin ou en tant qu'argument de ligne de commande.*
le fait considérer un caractère à la fois plutôt qu'une ligne à la fois.count = Counter(…)
compte les occurrences de chaque caractère efficacement, en un seul passage, et stocke le résultat dans la variable count
.La deuxième ligne affiche simplement les résultats.
'{} - {}'.format(c, count[c] + count[c.upper()]) for c in string.ascii_lowercase
dresse une liste de chaque caractère et de son nombre.print(',\n'.join(…))
le met dans le format souhaité: un par ligne, séparés par des virgules, mais pas de virgule sur la dernière ligne.