提问者:小点点

Perl中的正则表达式被括号弄乱了


我是perl新手,最近遇到了以下问题。

我有一个格式为“$num1$num2$num3$num4”的字符串,$num1、$num2、$num3、$num4是实数,可以是科学数字,也可以是常规格式。

现在,我想使用正则表达式从字符串中提取4个数字。

$real_num = '\s*([+-]?[0-9]+\.?[0-9]*([eE][+-]?[0-9]+)?)'
while (<FP>) {
    if (/$real_num$real_num$real_num$real_num/) {
        print $1; print $2; print$3; print$4;
    }
}

如何从1美元、2美元、3美元和4美元中获得num1美元、num2美元、num3美元、num4美元?由于$real\u num正则表达式中有一个必要的括号,所以$1、2、3、4不是我现在所期望的。

感谢所有热情的回复,非捕获组是我需要的答案!


共3个答案

匿名用户

只需在$real_num正则表达式中使用非捕获组,并使正则表达式本身成为捕获组:

$real_num = '\s*([+-]?[0-9]+\.?[0-9]*(?:[eE][+-]?[0-9]+)?)'

现在,问题是:/$real\u num$real\u num$real\u num$real\u num/如果有4个以上的数字,很容易失败。也许现在不是这样。但是,你也应该注意这一点。拆分是更好的选择。

匿名用户

如果您确定您的行包含数字,您可以使用拆分函数来避免该正则表达式:

while (<FP>) {
    my @numbers = split /\s+/; #<-- an array with the parsed numbers
}

如果需要检查提取的字符串是否真的是数字,请使用标量::Util looks\u like\u number。例子:

use strict;
use warnings;
use Scalar::Util qw/looks_like_number/;

while(<DATA>) {
    my @numbers = split /\s+/;
    @numbers = map { looks_like_number($_) ? $_ : undef } @numbers;
    say "@numbers";
}


__DATA__
1 2 NaN 4 -1.23
5 6 f 8 1.32e12

打印:

1 2 NaN 4 -1.23
5 6  8 1.32e12

匿名用户

两个重要问题的答案将影响您是否需要使用正则表达式来匹配各种数字格式,或者您是否可以做一些更简单的事情:

  1. 您确定您的行只包含数字还是也包含其他数据(或者可能有些行根本没有数字,只包含其他数据)
  2. 您确定所有数字和/或其他数据之间至少用一个空格隔开吗?如果没有,它们是如何分开的?(例如,portsnap fetch的输出会生成大量类似3690…3700…的数字,这些数字带有小数点,并且没有空格来分隔它们

如果行仅包含数字而不包含其他数据,并且数字之间用空格分隔,则无需检查结果是否为数字,只需将行拆分:

my @numbers = split /\s+/;

如果您不确定您的行是否包含数字,但您确定每个数字与其他数字或其他数据之间至少有一个空格,那么下一行代码是正确提取数字的一种非常好的方法,它使用一种巧妙的方式允许Perl本身识别所有许多不同的数字合法格式。(这假设您不想将其他数据值转换为NaN。)@number的结果将是正确识别当前输入行中的所有数字。

my @numbers = grep { 1*$_ eq $_ } m/(\S*\d\S*)/g;
# we could do simply a split, but this is more efficient because when
# non-numeric data is present, it will only perform the number
# validation on data pieces that actually do contain at least one digit

通过检查表达式的真值,可以确定是否至少存在一个数字

如果你的数字相互碰撞,例如5.17e 7-4.0e-1,那么你的日子会更难过。这是唯一一次需要复杂的正则表达式。

注意:更新的代码更快/更好。

注2:由于存储undef的值时map的工作方式很微妙,所以得票最多的答案存在问题。当使用该程序从HTTP日志文件等第一行数据中提取数字时,可以通过该程序的输出来说明这一点。输出看起来是正确的,但数组实际上有许多空元素,并且不能像预期的那样找到存储在$numbers[0]中的第一个数字。事实上,这是全部输出:

$ head -1 http | perl prog1.pl
Use of uninitialized value $numbers[0] in join or string at prog1.pl line 8, <> line 1.
Use of uninitialized value $numbers[1] in join or string at prog1.pl line 8, <> line 1.
Use of uninitialized value $numbers[2] in join or string at prog1.pl line 8, <> line 1.
Use of uninitialized value $numbers[3] in join or string at prog1.pl line 8, <> line 1.
Use of uninitialized value $numbers[4] in join or string at prog1.pl line 8, <> line 1.
Use of uninitialized value $numbers[5] in join or string at prog1.pl line 8, <> line 1.
Use of uninitialized value $numbers[6] in join or string at prog1.pl line 8, <> line 1.
Use of uninitialized value $numbers[7] in join or string at prog1.pl line 8, <> line 1.
Use of uninitialized value $numbers[10] in join or string at prog1.pl line 8, <> line 1.
Use of uninitialized value $numbers[11] in join or string at prog1.pl line 8, <> line 1.
Use of uninitialized value $numbers[12] in join or string at prog1.pl line 8, <> line 1.
Use of uninitialized value $numbers[13] in join or string at prog1.pl line 8, <> line 1.
Use of uninitialized value $numbers[14] in join or string at prog1.pl line 8, <> line 1.
Use of uninitialized value $numbers[15] in join or string at prog1.pl line 8, <> line 1.
Use of uninitialized value $numbers[16] in join or string at prog1.pl line 8, <> line 1.
        200 2206

(请注意,这些数字的缩进显示了当数组转换为字符串时,有多少空数组元素出现在数字中,并且在实际数字之前用空格连接在一起。)

但是,我的解决方案在视觉上和实际数组内容中都产生了正确的结果,即$number[0]、$number[1]等,实际上是数据文件行中包含的第一个和第二个数字。

while (<>) {
my @numbers = m/(\S*\d\S*)/g;
@numbers = grep { $_ eq 1*$_ } @numbers;
print "@numbers\n";
}

$head-1超文本传输协议|perlprog2.pl

200 2206

此外,使用slow library函数会使另一个解决方案的运行速度降低50%。在10000行数据上运行程序时,输出完全相同。