218 votes

Comment faire rapidement la somme de tous les chiffres d'un fichier ?

J'ai un fichier qui contient plusieurs milliers de chiffres, chacun sur sa propre ligne :

34
42
11
6
2
99
...

Je cherche à écrire un script qui va imprimer la somme de tous les nombres dans le fichier. J'ai une solution, mais elle n'est pas très efficace. (Il faut plusieurs minutes pour l'exécuter.) Je suis à la recherche d'une solution plus efficace. Avez-vous des suggestions ?

5 votes

Quelle a été votre solution lente ? Peut-être pouvons-nous vous aider à trouver ce qui était lent :)

4 votes

@brian d foy, je suis trop embarrassé pour le poster. Je sais pourquoi c'est lent. C'est parce que j'appelle "cat filename | head -n 1" pour obtenir le chiffre du haut, l'ajouter à un total courant, et appeler "cat filename | tail..." pour enlever la ligne du haut pour la prochaine itération... J'ai beaucoup à apprendre sur la programmation ! !!

6 votes

C'est... très systématique. Très clair et direct, et je l'aime pour tout ce que c'est qu'une horrible abomination. Construit, je suppose, à partir des outils que vous connaissiez quand vous avez commencé, non ?

414voto

Ayman Hourieh Points 39435

Vous pouvez utiliser awk :

awk '{ sum += $1 } END { print sum }' file

4 votes

Programme dépassé : nombre maximum de champs : 32767

1 votes

Avec le -F '\t' si vos champs contiennent des espaces et sont séparés par des tabulations.

7 votes

Veuillez marquer cette réponse comme étant la meilleure. Cela fonctionne également si vous voulez additionner la première valeur de chaque ligne, dans un fichier TSV (tab-separated value).

118voto

devnull Points 45016

Aucune des solutions proposées jusqu'à présent n'utilise paste . En voici un :

paste -sd+ filename | bc

À titre d'exemple, calculez Σn où 1<=n<=100000 :

$ seq 100000 | paste -sd+ | bc -l
5000050000

(Pour les curieux, seq n imprimerait une séquence de chiffres de 1 à n étant donné un nombre positif n .)

1 votes

Très bien ! Et facile à retenir

1 votes

seq 100000 | paste -sd+ - | bc -l sur le shell Bash de Mac OS X. Et c'est de loin la solution la plus douce et la plus unixe !

2 votes

@SimoA. Je vote pour que nous utilisions le terme unixiest à la place de unixest car la solution la plus sexy est toujours la plus unixest ;)

115voto

brian d foy Points 71781

Pour une ligne unique en Perl, c'est en fait la même chose que la fonction awk solution dans La réponse de Ayman Hourieh :

 % perl -nle '$sum += $_ } END { print $sum'

Si vous êtes curieux de savoir ce que font les one-liners Perl, vous pouvez les dépareiller :

 %  perl -MO=Deparse -nle '$sum += $_ } END { print $sum'

Le résultat est une version plus verbeuse du programme, dans une forme que personne n'écrirait jamais par lui-même :

BEGIN { $/ = "\n"; $\ = "\n"; }
LINE: while (defined($_ = <ARGV>)) {
    chomp $_;
    $sum += $_;
}
sub END {
    print $sum;
}
-e syntax OK

Juste pour rire, j'ai essayé avec un fichier contenant 1 000 000 de chiffres (dans la plage 0 - 9 999). Sur mon Mac Pro, le résultat est pratiquement instantané. C'est dommage, parce que j'espérais que l'utilisation de mmap serait vraiment rapide, mais c'est juste le même temps :

use 5.010;
use File::Map qw(map_file);

map_file my $map, $ARGV[0];

$sum += $1 while $map =~ m/(\d+)/g;

say $sum;

4 votes

Wow, ça montre une profond compréhension du code que -nle enroule réellement autour de la chaîne que vous lui donnez. J'ai d'abord pensé que vous ne devriez pas poster en état d'ébriété, puis j'ai vu qui vous étiez et je me suis souvenu de certaines de vos autres réponses sur Perl :-)

0 votes

-n et -p mettent juste des caractères autour de l'argument de -e, donc vous pouvez utiliser ces caractères pour ce que vous voulez. Nous avons beaucoup d'expressions en une ligne qui font des choses intéressantes avec ça en Programmation efficace en Perl (qui est sur le point d'arriver sur les étagères).

5 votes

Sympa, c'est quoi ces accolades non assorties ?

90voto

glenn jackman Points 69748

Juste pour le plaisir, faisons une analyse comparative :

$ for ((i=0; i<1000000; i++)) ; do echo $RANDOM; done > random_numbers

$ time perl -nle '$sum += $_ } END { print $sum' random_numbers
16379866392

real    0m0.226s
user    0m0.219s
sys     0m0.002s

$ time awk '{ sum += $1 } END { print sum }' random_numbers
16379866392

real    0m0.311s
user    0m0.304s
sys     0m0.005s

$ time { { tr "\n" + < random_numbers ; echo 0; } | bc; }
16379866392

real    0m0.445s
user    0m0.438s
sys     0m0.024s

$ time { s=0;while read l; do s=$((s+$l));done<random_numbers;echo $s; }
16379866392

real    0m9.309s
user    0m8.404s
sys     0m0.887s

$ time { s=0;while read l; do ((s+=l));done<random_numbers;echo $s; }
16379866392

real    0m7.191s
user    0m6.402s
sys     0m0.776s

$ time { sed ':a;N;s/\n/+/;ta' random_numbers|bc; }
^C

real    4m53.413s
user    4m52.584s
sys 0m0.052s

J'ai abandonné l'exécution de sed après 5 minutes


J'ai plongé pour lua et il est rapide :

$ time lua -e 'sum=0; for line in io.lines() do sum=sum+line end; print(sum)' < random_numbers
16388542582.0

real    0m0.362s
user    0m0.313s
sys     0m0.063s

et pendant que je mets à jour ça, ruby :

$ time ruby -e 'sum = 0; File.foreach(ARGV.shift) {|line| sum+=line.to_i}; puts sum' random_numbers
16388542582

real    0m0.378s
user    0m0.297s
sys     0m0.078s

Suivez le conseil d'Ed Morton : utiliser $1

$ time awk '{ sum += $1 } END { print sum }' random_numbers
16388542582

real    0m0.421s
user    0m0.359s
sys     0m0.063s

contre l'utilisation $0

$ time awk '{ sum += $0 } END { print sum }' random_numbers
16388542582

real    0m0.302s
user    0m0.234s
sys     0m0.063s

18 votes

+1 : Pour avoir trouvé un tas de solutions, et les avoir évaluées.

0 votes

Time cat random_numbers|paste -sd+|bc -l real 0m0.317s user 0m0.310s sys 0m0.013s

0 votes

Qui devrait être à peu près identique à la tr solution.

7voto

lhf Points 30556

Voici une autre phrase

( echo 0 ; sed 's/$/ +/' foo ; echo p ) | dc

Cela suppose que les nombres sont des entiers. Si vous avez besoin de décimales, essayez

( echo 0 2k ; sed 's/$/ +/' foo ; echo p ) | dc

Ajustez 2 au nombre de décimales nécessaires.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X