当前位置: 首页 > 知识库问答 >
问题:

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

杭永安
2023-03-14

我是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个答案

何海
2023-03-14

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

  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日志html" target="_blank">文件等第一行数据中提取数字时,可以通过该程序的输出来说明这一点。输出看起来是正确的,但数组实际上有许多空元素,并且不能像预期的那样找到存储在$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行数据上运行程序时,输出完全相同。

咸亦
2023-03-14

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

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
通和裕
2023-03-14

只需在$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个以上的数字,很容易失败。也许现在不是这样。但是,你也应该注意这一点。拆分是更好的选择。

 类似资料:
  • 我试图匹配,使用正则表达式,所有逗号(后跟一个空格):在任何括号或方括号之外,即逗号不应该包含在括号或方括号中。 目标字符串是。在这种情况下,它应该匹配第一个逗号和最后一个逗号(介于和之间,和)。 所以我可以拆分分成,and,不会因此而使括号/括号不平衡。 为此,似乎很难单独使用正则表达式。有没有其他办法解决这个问题? 我使用的正则表达式: 但是这个表达式将匹配另外两个不应该匹配的逗号(第二个和第

  • 我需要通过不在括号中的管道拆分文本。这是示例文本 我在这里找到了这个/\ |(?![^{]*})/g:regex,提取字符串,不要在两个括号之间 现在当我想用管子分割这部分字符串时 它还选择杰森、玛丽亚和圣诞老人之间的管道,因为它们后面有一个开口支架。如果正则表达式不在任何括号中,如何将其更改为仅匹配管道。 测试字符串: 应该返回 . 应该返回

  • 主要内容:匹配操作符,实例,正则表达式变量,实例,替换操作符,实例,转化操作符,实例,更多正则表达式规则正则表达式(regular expression)描述了一种字符串匹配的模式,可以用来检查一个串是否含有某种子串、将匹配的子串做替换或者从某个串中取出符合某个条件的子串等。 Perl语言的正则表达式功能非常强大,基本上是常用语言中最强大的,很多语言设计正则式支持的时候都参考Perl的正则表达式。 Perl的正则表达式的三种形式,分别是匹配,替换和转化: 匹配:m/ /(还可以简写为/ /,略去

  • 正则表达式(regular expression)描述了一种字符串匹配的模式,可以用来检查一个串是否含有某种子串、将匹配的子串做替换或者从某个串中取出符合某个条件的子串等。 Perl语言的正则表达式功能非常强大,基本上是常用语言中最强大的,很多语言设计正则式支持的时候都参考Perl的正则表达式。 Perl的正则表达式的三种形式,分别是匹配,替换和转化: 匹配:m//(还可以简写为//,略去m) 替

  • 问题内容: 匹配字符串中的’(’的正则表达式是什么? 以下是场景: 我有一串 我想使用正则表达式拆分字符串。为此,我正在使用 但是我收到以下异常。 转义似乎不起作用。 问题答案: 两种选择: 首先,你可以使用转义 回 斜线- 另外,由于它是单个字符,因此您可以将其放入不需要转义的字符类中-

  • 问题内容: 我有类似的东西 商店 结束行像1商店..我想匹配,它使用python正则表达式。 我尝试了类似的方法, 但是没有用。 编辑:添加代码,我试过 请帮助。 谢谢吉乔 问题答案: 或多或少直接回复您的评论 尝试这个 解决方案是使用,而不是使用后者,因为后者试图将整个字符串与regexp匹配,而前者只是试图在字符串内部查找与表达式匹配的子字符串。