加载字段 1 并在 END{} 处打印,相当于 Perl 中的 awk

Load field 1 and print at the END{} equivalent awk in Perl

我有以下 AWK 脚本,它计算字段 1 中元素的出现次数,并在完成读取整个文件时打印每个元素和重复次数。

awk '{a[]++} END{ for(i in a){print i"-->"a[i]} }' file

我是 perl 的新手,我不知道如何等效。到目前为止,我所拥有的在下面,但它的语法不正确。提前致谢。

perl -lane '$a{$F[1]}++ END{foreach $a {print $a} }' file

____________________________________UPDATE ______________________________________

您好,感谢您的回答。实际输入文件有 3400 万行,执行时间比 awk 和 Perl 快 3 倍或更多。 awk 比 perl 快吗?

awk '{a[]++}END{for(i in a){print i"-->"a[i]}}' file #--> 2:45 aprox
perl -lane '$a{$F[0]}++;END{foreach my $k (keys %a){ print "$k --> $a{$k}" } }' file #--> 7 min aprox
perl -lanE'$a{$F[0]}++; END { say "$_ => $a{$_}" for keys %a }' file # -->9 min aprox

相当于你的awk

perl -lanE'$a{$F[0]}++; END { say "$_ => $a{$_}" for keys %a }' file

通过-a,该行被分解为@F中的字段,因此您希望$F[0]作为散列%a中的键,其值由处理的计数器++。散列在键上迭代并打印在 END 块中。

不过,效率比较就上来了。改进这一点的一种方法是不获取行中的所有字段,使用 -a 完成,因为只需要第一个。在想到的两种方式之间

perl -nE'$a{(/(\S+)/)[0]}++; END { ... }' 

perl -nE'$a{(split " ", $_, 2)[0]}++; END { ... }'

split 在 8M 行的文件中,其 3.63s 与正则表达式的 4.41s 相比,速度明显更快。

对于您的 awk 行,这仍然落后于 1.99s。所以看起来 awk 对于这个任务更快。


我对 800 万行文件的计时总结(几次运行的平均值)

awk  (question)  1.99s
perl (split)     3.63s
perl (regex)     4.41s
perl (like awk)  5.61s

这些时间在运行过程中会相差几十毫秒(几 0.01 秒)。

你可以通过a2p

$ cat file
1
1
2
3
3
3

$ perl -lane '$a{$F[0]}++;END{foreach my $k (keys %a){ print "$k --> $a{$k}" } }' file
1 --> 2
2 --> 1
3 --> 3

$ awk '{a[]++} END{ for(i in a){print i" --> "a[i]} }' file
1 --> 2
2 --> 1
3 --> 3

这个破坏性的方法是我想出最快的:

perl -lne '$_=~s/\s.*//; $a{$_}++; END{foreach $i (keys %a){print "$i-->$a{$i}"}}' file

然而,它仍然不如 awk 快。

输入文件显然会有所不同,但在我的 3350 万行测试文件中,Perl 5.22.1 比下面的 Awk 4.1.3 稍快(12.23 对 12.52 秒)。

schumack@daddyo2 10-02T18:25:17 54> wc -l listbig
33521910 listbig

schumack@daddyo2 10-02T18:25:58 55> /usr/bin/time -f '%E %P' awk '{a[]++} END{ for(i in a){print i"-->"a[i]} }' listbig
1-->9434310
2-->1605840
3-->9635040
4-->5218980
5-->4416060
7-->802920
8-->802920
9-->802920
12-->802920
0:12.52 99%

schumack@daddyo2 10-02T18:26:17 56> /usr/bin/time -f '%E %P' perl -lne '$_=~s/^(\S+) .*//; $a{$_}++; END{foreach $i (keys %a){print "$i-->$a{$i}"}}' listbig 
1-->9434310
5-->4416060
2-->1605840
3-->9635040
12-->802920
8-->802920
9-->802920
4-->5218980
7-->802920
0:12.23 99%

好的,Ger,再来一次 :-) 我将我的 Perl 升级到我可以使用的最新版本并制作了一个如您所描述的文件(3450 万行,每行在第一列也是唯一一列中有一个 16 位整数):

schumack@linux2 52> wc -l listbig
34521909 listbig

schumack@linux2 53> head -3 listbig
1111111111111111
3333333333333333
4444444444444444

然后我 运行 一个专门的 Perl 行(适用于此文件,但与 awk 行不同)。和以前一样,我使用 /usr/bin/time 对运行进行计时:

schumack@linux2 54> /usr/bin/time -f '%E %P' /usr/local/bin/perl -lne 'chomp; $a{$_}++; END{foreach $i (keys %a){print "$i-->$a{$i}"}}' listbig
5555555555555555-->4547796
1111111111111111-->9715747
9999999999999999-->826872
3333333333333333-->9922465
1212121212121212-->826872
4444444444444444-->5374669
2222222222222222-->1653744
8888888888888888-->826872
7777777777777777-->826872
0:12.20 99%

schumack@linux2 55> /usr/bin/time -f '%E %P' awk '{a[]++} END{ for(i in a){print i"-->"a[i]} }' listbig
1111111111111111-->9715747
2222222222222222-->1653744
3333333333333333-->9922465
4444444444444444-->5374669
5555555555555555-->4547796
1212121212121212-->826872
7777777777777777-->826872
8888888888888888-->826872
9999999999999999-->826872
0:12.61 99%

perl 和 awk 运行 在 3450 万行文件上都非常快,彼此相差不到半秒。 好奇什么类型的机器 / OS / 你目前使用的 Perl 版本。我在一台大约有 4 年历史的华硕笔记本电脑上进行了测试,它配备了 Intel I7。我正在使用 Ubuntu 16.04 和 Perl v5.26.1

无论如何,谢谢你让我玩 Perl!

玩得开心, 肯