两个重要问题的答案将影响您是否需要使用正则表达式来匹配各种数字格式,或者您是否可以做一些更简单的事情:
- 您确定您的行仅包含数字还是它们还包含其他数据(或者可能某些行根本没有数字而只有其他数据)?
- 您确定所有数字彼此和/或其他数据之间至少有一个空格隔开吗?如果不是,它们是如何分开的?(例如,输出
portsnap fetch
生成很多像这样的数字 3690....3700.... 带有小数点并且根本没有用于分隔它们的空格。
如果您的行只包含数字而没有其他数据,并且数字用空格分隔,那么您甚至不需要检查结果是否为数字,而只需将行分开:
my @numbers = split /\s+/;
如果您不确定您的行是否包含数字,但您确定每个数字与其他数字或其他数据之间至少有一个空格,那么下一行代码是一种非常好的方法,可以巧妙地正确提取数字允许 Perl 本身识别所有许多不同的合法数字格式的方法。(这假设您不想将其他数据值转换为NaN
。)结果@numbers
将正确识别当前输入行中的所有数字。
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
您可以通过检查表达式的真值来确定是否至少存在一个数字,以及通过使用条件等@numbers > 1
来确定是否恰好存在四个。@numbers == 4
如果您的数字相互碰撞,例如 5.17e+7-4.0e-1,那么您将遇到更困难的时期。那是您唯一需要复杂正则表达式的时候。
注意:更新的代码更快/更好。
注意 2:由于 map 在存储 undef 值时的工作方式很微妙,因此投票最多的答案存在问题。当使用该程序从第一行数据(例如 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
(请注意,当数组转换为字符串时,这些数字的缩进显示了在实际数字之前存在多少个空数组元素@numbers
并用空格连接在一起。)
但是,我的解决方案在视觉和实际数组内容中都产生了正确的结果,即 $numbers[0]、$number[1] 等实际上是数据文件行中包含的第一个和第二个数字。
while (<>) {
my @numbers = m/(\S*\d\S*)/g;
@numbers = grep { $_ eq 1*$_ } @numbers;
print "@numbers\n";
}
$ head -1 http | perl prog2.pl
200 2206
此外,使用 slow 库函数会使其他解决方案的运行速度降低 50%。在 10,000 行数据上运行程序时,输出在其他方面是相同的。