如何判断Perl正则表达式模式中哪些替代项匹配?

时间:2011-11-09 17:28:25

标签: regex perl capture regex-group

我有一个正则表达式列表(大约10 - 15),我需要匹配一些文本。在循环中逐个匹配它们太慢了。但是,我没有编写自己的状态机来同时匹配所有正则表达式,而是尝试|单个正则表达式并让perl完成工作。问题是我如何知道哪些替代方案匹配?

这个问题解决了每个正则表达式中没有捕获组的情况。 (which portion is matched by regex?)如果每个正则表达式中都有捕获组,该怎么办?

以下是

/^(A(\d+))|(B(\d+))|(C(\d+))$/

和字符串“A123”,我怎么能知道A123匹配并提取“123”?

5 个答案:

答案 0 :(得分:5)

为什么不使用/^ (?<prefix> A|B|C) (?<digits> \d+) $/x。注意,命名捕获组用于清晰,而不是必需的。

答案 1 :(得分:5)

您不需要编写自己的状态机来组合正则表达式。看看Regexp:Assemble。它有一些方法可以跟踪哪些初始模式匹配。

编辑:

use strict;
use warnings;

use 5.012;

use Regexp::Assemble;

my $string = 'A123';

my $re = Regexp::Assemble->new(track => 1);
for my $pattern (qw/ A(\d+) B(\d+) C(\d+) /) {
  $re->add($pattern);
}

say $re->re; ### (?-xism:(?:A(\d+)(?{0})|B(\d+)(?{2})|C(\d+)(?{1})))
say for $re->match($string); ### A(\d+)
say for $re->capture; ### 123

答案 2 :(得分:2)

A123将位于捕获组$1中,123将位于组$2

所以你可以说:

if ( /^(A(\d+))|(B(\d+))|(C(\d+))$/ && $1 eq 'A123' && $2 eq '123' ) {
    ...
}

这是多余的,但你明白了......

编辑:不,您不必枚举每个子匹配,您询问如何知道A123是否匹配以及如何提取123

  • 除非if匹配
  • ,否则您不会输入A123阻止
  • 您可以使用123反向引用来提取$2

所以也许这个例子会更清楚:

if ( /^(A(\d+))|(B(\d+))|(C(\d+))$/ ) {
    # do something with $2, which will be '123' assuming $_ matches /^A123/
}

编辑2:

要捕获AoA中的匹配(这是一个不同的问题,但这应该这样做):

#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;

my @matches = map { [$1,$2] if /^(?:(A|B|C)(\d+))$/ } <DATA>;
print Dumper \@matches;

__DATA__
A123
B456
C769

结果:

$VAR1 = [
          [
            'A',
            '123'
          ],
          [
            'B',
            '456'
          ],
          [
            'C',
            '769'
          ]
        ];

请注意,我修改了你的正则表达式,但看起来你的评论就是这样......

答案 3 :(得分:1)

使用您的示例数据,可以轻松编写

'A123' =~ /^([ABC])(\d+)$/;

之后$ 1将包含前缀,$ 2将包含后缀。

我无法判断这是否与您的实际数据相关,但是使用额外的模块似乎有些过分。

答案 4 :(得分:0)

你可以在Perl中做的另一件事是使用&#34;(?{...})&#34;直接在你的正则表达式中嵌入Perl代码。因此,您可以设置一个变量来告诉您正则表达式的哪个部分匹配。警告:你的正则表达式不应该包含任何变量(在嵌入的Perl代码之外),它将被插入到正则表达式中,否则你将得到错误。以下是使用此功能的示例解析器:

my $kind;
my $REGEX  = qr/
          [A-Za-z][\w]*                        (?{$kind = 'IDENT';})
        | (?: ==? | != | <=? | >=? )           (?{$kind = 'OP';})
        | -?\d+                                (?{$kind = 'INT';})
        | \x27 ( (?:[^\x27] | \x27{2})* ) \x27 (?{$kind = 'STRING';})
        | \S                                   (?{$kind = 'OTHER';})
        /xs;

my $line = "if (x == 'that') then x = -23 and y = 'say ''hi'' for me';";
my @tokens;
while ($line =~ /( $REGEX )/xsg) {
    my($match, $str) = ($1,$2);
    if ($kind eq 'STRING') {
        $str =~ s/\x27\x27/\x27/g;
        push(@tokens, ['STRING', $str]);
        }
    else {
        push(@tokens, [$kind, $match]);
        }
    }
foreach my $lItems (@tokens) {
    print("$lItems->[0]: $lItems->[1]\n");
    }

打印出以下内容:

IDENT: if
OTHER: (
IDENT: x
OP: ==
STRING: that
OTHER: )
IDENT: then
IDENT: x
OP: =
INT: -23
IDENT: and
IDENT: y
OP: =
STRING: say 'hi' for me
OTHER: ;

它有点做作,但是你会注意到字符串周围的引号(实际上是撇号)被剥离了(连续引号也折叠成单引号),所以一般来说,只有$ kind变量将告诉您解析器是否看到了标识符或带引号的字符串。