How can I quickly sum all numbers in a file?
我有一个包含几千个数字的文件,每个数字都在自己的行中:
1 2 3 4 5 6 7 | 34 42 11 6 2 99 ... |
我正在写一个脚本,它将打印文件中所有数字的总和。我有解决办法,但效率不高。(运行需要几分钟)我正在寻找更有效的解决方案。有什么建议吗?
你可以用锥子:
1 | awk '{ sum += $1 } END { print sum }' file |
对于一个PerlOne线性,它基本上与Ayman Hourieh的答案中的
1 | % perl -nle '$sum += $_ } END { print $sum' |
如果您想知道PerlOne的行程序是做什么的,可以将它们放在下面:
1 | % perl -MO=Deparse -nle '$sum += $_ } END { print $sum' |
结果是程序的一个更详细的版本,形式是没有人会自己写:
1 2 3 4 5 6 7 8 9 10 11 |
只是为了咯咯笑,我用一个包含1000000个数字的文件(在0-9999范围内)尝试了这个方法。在我的Mac Pro上,它几乎瞬间返回。这太糟糕了,因为我希望使用
1 2 3 4 5 6 7 8 | use 5.010; use File::Map qw(map_file); map_file my $map, $ARGV[0]; $sum += $1 while $map =~ m/(\d+)/g; say $sum; |
到目前为止,没有一种解决方案使用
1 | paste -sd+ filename | bc |
例如,计算∑n,其中1<=n<=100000:
1 2 | $ seq 100000 | paste -sd+ | bc -l 5000050000 |
(出于好奇,
为了好玩,让我们来测试一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | $ 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" " + < 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/ /+/;ta' random_numbers|bc; } ^C real 4m53.413s user 4m52.584s sys 0m0.052s |
我在5分钟后中止了SED的运行
我一直潜到卢亚,而且速度很快:
1 2 3 4 5 6 | $ 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 |
当我更新这个的时候,Ruby:
1 2 3 4 5 6 | $ 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 |
听从艾德莫顿的建议:使用
1 2 3 4 5 6 | $ time awk '{ sum += $1 } END { print sum }' random_numbers 16388542582 real 0m0.421s user 0m0.359s sys 0m0.063s |
vs使用
1 2 3 4 5 6 | $ time awk '{ sum += $0 } END { print sum }' random_numbers 16388542582 real 0m0.302s user 0m0.234s sys 0m0.063s |
这工作:
1 2 |
另一种选择是使用
1 2 |
这是一个直接的重击:
1 2 3 4 5 6 |
这是另一条单线
1 | ( echo 0 ; sed 's/$/ +/' foo ; echo p ) | dc |
这假设数字是整数。如果您需要小数,请尝试
1 | ( echo 0 2k ; sed 's/$/ +/' foo ; echo p ) | dc |
将2调整为所需的小数位数。
我更喜欢将GNU数据mash用于此类任务,因为它比Perl或Awk更简洁易读。例如
1 | datamash sum 1 < myfile |
其中1表示数据的第一列。
1 | cat nums | perl -ne '$sum += $_ } { print $sum' |
(与Brian D Foy的回答相同,没有‘结束’)
为了好玩,让我们用pdl,Perl的数组数学引擎来做吧!
1 | perl -MPDL -E 'say rcols(shift)->sum' datafile |
下面是一个使用带有生成器表达式的Python的解决方案。在我的旧电脑上测试了一百万个数字。
1 2 3 4 5 | time python -c"import sys; print sum((float(l) for l in sys.stdin))" < file real 0m0.619s user 0m0.512s sys 0m0.028s |
我更喜欢用r来表示:
1 | $ R -e 'sum(scan("filename"))' |
1 | $ perl -MList::Util=sum -le 'print sum <>' nums.txt |
1 2 | sed ':a;N;s/ /+/;ta' file|bc |
更简洁:
1 2 3 4 5 | # Ruby ruby -e 'puts open("random_numbers").map(&:to_i).reduce(:+)' # Python python -c 'print(sum(int(l) for l in open("random_numbers")))' |
Perl & Nbsp;6
1 | say sum lines |
1 2 3 4 | ~$ perl6 -e '.say for 0..1000000' > test.in ~$ perl6 -e 'say sum lines' < test.in 500000500000 |
与露比:
1 | ruby -e"File.read('file.txt').split.inject(0){|mem, obj| mem += obj.to_f}" |
另一个娱乐
1 | sum=0;for i in $(cat file);do sum=$((sum+$i));done;echo $sum |
或者只是一次狂欢
但awk解决方案可能是最好的,因为它是最紧凑的。
我没有测试过这个,但是它应该可以工作:
1 2 3 |
如果BC不处理EOF和EOL,您可能需要在BC之前向字符串添加""(如通过echo)。
考虑到你需要通读整个文件,我不知道你是否能比这更好。
1 2 3 4 5 |
您可以使用alacon命令行实用程序对alasql数据库执行此操作。
它与node.js一起工作,因此需要先安装node.js,然后安装alasql包:
要从txt文件计算总和,可以使用以下命令:
1 | > node alacon"SELECT VALUE SUM([0]) FROM TXT('mydata.txt')" |
这里还有一个:
1 2 3 4 5 6 7 8 9 |
用
1 | (sed -e"s/$/+/" file; echo 0)|irb |
如果没有
1 2 |
C总是以速度取胜:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
1米数字的计时(与我的python答案相同的机器/输入):
1 2 3 4 5 | $ gcc sum.c -o sum && time ./sum < numbers 5003371677.000000 real 0m0.188s user 0m0.180s sys 0m0.000s |
cat f | tr"
""+" | perl -pne chop | R --vanilla --slave
可笑的是: