在Perl中使用正则表达式匹配创建哈希表

3
假设我有一个像下面这样的文件:
我想将所有的十进制数存储在哈希表中。
hello world 10 20
world 10 10 10 10 hello 20
hello 30 20 10 world 10

我正在查看 this,这个很好用:
> perl -lne 'push @a,/\d+/g;END{print "@a"}' temp
10 20 10 10 10 10 20 30 20 10 10

我需要做的是统计每个正则表达式出现的次数。

为此,我认为最好将所有匹配项存储在哈希表中,并为每个键分配递增的值。

所以我尝试了:

perl -lne '$a{$1}++ for ($_=~/(\d+)/g);END{foreach(keys %a){print "$_.$a{$_}"}}' temp

这将给我一个输出:

> perl -lne '$a{$1}++ for ($_=~/(\d+)/g);END{foreach(keys %a){print "$_.$a{$_}"}}' temp
10.4
20.7

有人能纠正我哪里错了吗?

我期望的输出是:

10.7
20.3
30.1

尽管我可以使用awk完成这个任务,但我只想用Perl来完成
此外,输出的顺序对我不重要。
2个回答

5
$a{$1}++ for ($_=~/(\d+)/g);

This should be

$a{$_}++ for ($_=~/(\d+)/g);

并且可以简化为

$a{$_}++ for /\d+/g;

原因是/\d+/g创建了一个匹配列表,然后被for迭代。当前元素位于$_中。我想$1将包含上一次匹配留在那里的任何内容,但这绝对不是您在此情况下要使用的内容。


4

另一个选项是这个:

$a{$1}++ while ($_=~/(\d+)/g);

这段代码实现了你预期的功能:在匹配成功时循环处理每一个匹配项。因此,$1 的值将是你所期望的。
为了更加清晰地说明区别:
在Perl中,带有单个参数的 for 循环表示“对列表中的每个元素执行某些操作”:
for (@array)
{
    #do something to each array element
}

在您的代码中,首先构建了一系列匹配项,只有在找到整个匹配列表后,才有机会对结果进行操作。随着列表的构建,$1 在每次匹配时都被重置,但是在运行代码时,它已经设置为该行上最后一个匹配项。这就是为什么您的结果不合理的原因。
另一方面,while 循环意味着“每次检查这个条件是否为真,并继续执行,直到条件变为假”。因此,在正则表达式的每个匹配项上都将执行 while 循环中的代码,$1 具有该匹配项的值。
Perl 中另一个重要的区别是文件处理。 for (<FILE>) { ... } 首先将整个文件读入内存,这是浪费的。建议改用 while (<FILE>),因为这样可以逐行遍历文件并仅保留所需信息。

1
但是为什么相同的代码在使用for循环时不起作用?而在使用while循环时却可以正常工作呢? - user1939168
这是对forwhile优秀的实际演示,虽然我可能会选择@melpomene的方法,因为我从来不确定在循环或传递时$1,$2等会做什么。 - G. Cito

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接