如何快速汇总文件中的所有数字?

Mar*_*rts 178 linux bash shell perl awk

我有一个包含数千个数字的文件,每个数字都在它自己的行上:

34
42
11
6
2
99
...
Run Code Online (Sandbox Code Playgroud)

我正在寻找一个脚本,它将打印文件中所有数字的总和.我有一个解决方案,但效率不高.(运行需要几分钟.)我正在寻找更有效的解决方案.有什么建议?

Aym*_*ieh 350

你可以使用awk:

awk '{ sum += $1 } END { print sum }' file
Run Code Online (Sandbox Code Playgroud)

  • 请将此标记为最佳答案.如果要在TSV(制表符分隔值)文件中对每行中的第一个值求和,它也可以工作. (5认同)
  • 超出程序:字段大小的最大数量:32767 (4认同)
  • 如果您的字段包含空格并由制表符分隔,请使用“-F '\t'”选项。 (2认同)

bri*_*foy 105

对于Perl 单线程,它与Ayman Hourieh的答案中awk解决方案基本相同:

 % perl -nle '$sum += $_ } END { print $sum'
Run Code Online (Sandbox Code Playgroud)

如果您对Perl单行所做的很好奇,您可以解析它们:

 %  perl -MO=Deparse -nle '$sum += $_ } END { print $sum'
Run Code Online (Sandbox Code Playgroud)

结果是一个更冗长的程序版本,其形式是任何人都不会自己写的:

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

只是为了咯咯笑,我试着用一个包含1,000,000个数字的文件(范围在0到9,999之间).在我的Mac Pro上,它几乎立即返回.这太糟糕了,因为我希望使用它mmap会非常快,但它只是在同一时间:

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

map_file my $map, $ARGV[0];

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

say $sum;
Run Code Online (Sandbox Code Playgroud)

  • -n在程序周围添加`while {}`循环.如果你把`} ... {`放在里面,那么你有`while {} ... {}`.邪恶?略. (15认同)
  • 哇,这显示了_deep_理解什么代码-nle实际上包裹你给它的字符串.我最初的想法是你不应该在陶醉时发帖,但后来我注意到你是谁,并记住你的其他一些Perl答案:-) (4认同)
  • 很好,这些不匹配的花括号是什么? (4认同)
  • 突出显示`-MO = Deparse`选项的大奖金!即使是在一个单独的主题上. (4认同)

dev*_*ull 93

迄今为止没有一种解决方案paste.这是一个:

paste -sd+ filename | bc
Run Code Online (Sandbox Code Playgroud)

例如,计算Σn,其中1 <= n <= 100000:

$ seq 100000 | paste -sd+ | bc -l
5000050000
Run Code Online (Sandbox Code Playgroud)

(对于好奇的人,seq n会打印一系列数字1,n给出一个正数n.)

  • `seq 100000 | 粘贴-sd+-| Mac OS X Bash shell 上的 bc -l`。这是迄今为止最甜蜜、最unix的解决方案! (2认同)
  • @SimoA。我投票认为我们使用术语 unixiest 来代替 unixest,因为最性感的解决方案总是最 unixest ;) (2认同)

gle*_*man 80

只是为了好玩,让我们对它进行基准测试:

$ 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
Run Code Online (Sandbox Code Playgroud)

我在5分钟后中止了sed运行

  • +1:提出一系列解决方案,并对它们进行基准测试. (18认同)
  • 如果您使用`$ 0`而不是`$ 1`,则awk脚本的执行速度会更快一些,因为如果脚本中特别提到了任何字段,则awk会进行字段拆分(显然会花费时间),否则不会执行。 (2认同)

nis*_*ama 16

另一种选择是使用jq:

$ seq 10|jq -s add
55
Run Code Online (Sandbox Code Playgroud)

-s(--slurp)将输入行读入数组.


Pau*_*ce. 9

这是直接的Bash:

sum=0
while read -r line
do
    (( sum += line ))
done < file
echo $sum
Run Code Online (Sandbox Code Playgroud)

  • 只要没有小数,这个就可以工作 (2认同)
  • 它可能是最慢的解决方案之一,因此不太适合大量数字。 (2认同)

lhf*_*lhf 7

这是另一个单行

( echo 0 ; sed 's/$/ +/' foo ; echo p ) | dc
Run Code Online (Sandbox Code Playgroud)

这假设数字是整数.如果您需要小数,请尝试

( echo 0 2k ; sed 's/$/ +/' foo ; echo p ) | dc
Run Code Online (Sandbox Code Playgroud)

将2调整为所需的小数位数.


Zai*_*aid 6

$ perl -MList::Util=sum -le 'print sum <>' nums.txt
Run Code Online (Sandbox Code Playgroud)


fed*_*orn 6

我更喜欢为此使用R:

$ R -e 'sum(scan("filename"))'
Run Code Online (Sandbox Code Playgroud)


her*_*ung 6

我更喜欢将 GNU datamash 用于此类任务,因为它比 perl 或 awk 更简洁易读。例如

datamash sum 1 < myfile
Run Code Online (Sandbox Code Playgroud)

其中 1 表示第一列数据。


dwu*_*urf 5

C 总是以速度取胜:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv) {
    ssize_t read;
    char *line = NULL;
    size_t len = 0;
    double sum = 0.0;

    while (read = getline(&line, &len, stdin) != -1) {
        sum += atof(line);
    }

    printf("%f\n", sum);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

1M 数字的计时(与我的 python 答案相同的机器/输入):

$ gcc sum.c -o sum && time ./sum < numbers 
5003371677.000000
real    0m0.188s
user    0m0.180s
sys     0m0.000s
Run Code Online (Sandbox Code Playgroud)


Vid*_*dul 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")))'
Run Code Online (Sandbox Code Playgroud)


Bra*_*ert 5

6

say sum lines
Run Code Online (Sandbox Code Playgroud)
~$ perl6 -e '.say for 0..1000000' > test.in

~$ perl6 -e 'say sum lines' < test.in
500000500000
Run Code Online (Sandbox Code Playgroud)


Pet*_*r K 5

我不能就这么过去……这是我的哈斯克尔俏皮话。它实际上非常可读:

sum <$> (read <$>) <$> lines <$> getContents
Run Code Online (Sandbox Code Playgroud)

不幸的是,没有办法ghci -e直接运行它,所以它需要 main 函数、打印和编译。

main = (sum <$> (read <$>) <$> lines <$> getContents) >>= print
Run Code Online (Sandbox Code Playgroud)

为了澄清起见,我们读取整个输入 ( getContents),将其分割为lines,read作为数字 和sum<$>isfmap运算符 - 我们使用它而不是通常的函数应用程序,因为这一切都发生在 IO 中。read需要一个额外的fmap,因为它也在列表中。

$ ghc sum.hs
[1 of 1] Compiling Main             ( sum.hs, sum.o )
Linking sum ...
$ ./sum 
1
2
4
^D
7
Run Code Online (Sandbox Code Playgroud)

这是一个奇怪的升级,使其可以与浮动一起使用:

main = ((0.0 + ) <$> sum <$> (read <$>) <$> lines <$> getContents) >>= print
Run Code Online (Sandbox Code Playgroud)
$ ./sum 
1.3
2.1
4.2
^D
7.6000000000000005
Run Code Online (Sandbox Code Playgroud)