我想解释一下Perl的正则表达式引擎

时间:2016-05-22 15:25:12

标签: regex perl

@Borodin更新

我已将此代码重写为我认为更易于理解的内容。 OP正在将bd等进行比较,并且我已将所有符号更改为更加不同的ASCII字符。结果等同于OP的原始代码

我已经手动检查了所有正则表达式模式,但我没有看到差异

#! /usr/local/bin/perl

use strict;
use warnings qw/ all FATAL /;

use List::Util 'max';

my @tests = (
    [ vvOHvXcvv => qr/ ^ ( (v*) O    | H? (v*) X )* c \2 $ /x ],
    [ vvOvXcvv  => qr/ ^ ( (v*) O    | H? (v*) X )* c \2 $ /x ],
    [ vvXHvXcvv => qr/ ^ ( (v*) X    | H? (v*) X )* c \2 $ /x ],
    [ vvXvXcvv  => qr/ ^ ( (v*) X    | H? (v*) X )* c \2 $ /x ],
    [ vvOHvXcvv => qr/ ^ ( (v*) [XO] | H? (v*) X )* c \2 $ /x ],
    [ vvOvXcvv  => qr/ ^ ( (v*) [XO] | H? (v*) X )* c \2 $ /x ],
    [ vvXHvXcvv => qr/ ^ ( (v*) [XO] | H? (v*) X )* c \2 $ /x ],
    [ vvXvXcvv  => qr/ ^ ( (v*) [XO] | H? (v*) X )* c \2 $ /x ],
);

my $w1 = max map length $_->[0], @tests;
my ($no, $yes) = ( 'MATCHES', "doesn't match" );
my $w2 = max map length, $no, $yes;

for my $test ( @tests ) {
    my ( $str, $re ) = @$test;

    printf "%-*s %-*s %s\n",
            $w1+2, qq{"$str"},
            $w2, $str =~ $re ? 'MATCHES' : "doesn't match",
            $re;
}

输出

"vvOHvXcvv" MATCHES       (?^x: ^ ( (v*) O    | H? (v*) X )* c \2 $ )
"vvOvXcvv"  MATCHES       (?^x: ^ ( (v*) O    | H? (v*) X )* c \2 $ )
"vvXHvXcvv" MATCHES       (?^x: ^ ( (v*) X    | H? (v*) X )* c \2 $ )
"vvXvXcvv"  doesn't match (?^x: ^ ( (v*) X    | H? (v*) X )* c \2 $ )
"vvOHvXcvv" doesn't match (?^x: ^ ( (v*) [XO] | H? (v*) X )* c \2 $ )
"vvOvXcvv"  doesn't match (?^x: ^ ( (v*) [XO] | H? (v*) X )* c \2 $ )
"vvXHvXcvv" doesn't match (?^x: ^ ( (v*) [XO] | H? (v*) X )* c \2 $ )
"vvXvXcvv"  doesn't match (?^x: ^ ( (v*) [XO] | H? (v*) X )* c \2 $ )


以下Perl程序针对使用反向引用的各种正则表达式模式测试一些字符串。它说明了我无法理解的行为。

$snum$rnum变量仅用于对输出中的字符串和模式进行编号,以便于阅读。唯一值得一读的是@test数组的内容。

#! /usr/local/bin/perl -w

use strict;
use warnings;

my @test = (
    [ "aadeabcaa", qr/^((a*)d|e?(a*)b)*c\2$/ ],
    [ "aadabcaa", qr/^((a*)d|e?(a*)b)*c\2$/ ],
    [ "aabeabcaa", qr/^((a*)b|e?(a*)b)*c\2$/ ],
    [ "aababcaa", qr/^((a*)b|e?(a*)b)*c\2$/ ],
    [ "aadeabcaa", qr/^((a*)[bd]|e?(a*)b)*c\2$/ ],
    [ "aadabcaa", qr/^((a*)[bd]|e?(a*)b)*c\2$/ ],
    [ "aabeabcaa", qr/^((a*)[bd]|e?(a*)b)*c\2$/ ],
    [ "aababcaa", qr/^((a*)[bd]|e?(a*)b)*c\2$/ ],
);

my %snum;
my %rnum;
my $lsnum;
my $lrnum;

for ( my $i = 0 ; $i < scalar(@test); $i++ ) {

    my $t = $test[$i];  my $s = $t->[0];  my $r = $t->[1];

    my $snum = ($snum{$s} //= $lsnum++);
    my $rnum = ($rnum{$r} //= $lrnum++);

    my $match = ($s =~ $r);

    print "test $i: (S$snum) $s" .
        ($match?" MATCHES ":" DOES NOT match ") .
        "(R$rnum) $r\n";
}

输出

test 0: (S0) aadeabcaa MATCHES (R0) (?^:^((a*)d|e?(a*)b)*c\2$)
test 1: (S1) aadabcaa MATCHES (R0) (?^:^((a*)d|e?(a*)b)*c\2$)
test 2: (S2) aabeabcaa MATCHES (R1) (?^:^((a*)b|e?(a*)b)*c\2$)
test 3: (S3) aababcaa DOES NOT match (R1) (?^:^((a*)b|e?(a*)b)*c\2$)
test 4: (S0) aadeabcaa DOES NOT match (R2) (?^:^((a*)[bd]|e?(a*)b)*c\2$)
test 5: (S1) aadabcaa DOES NOT match (R2) (?^:^((a*)[bd]|e?(a*)b)*c\2$)
test 6: (S2) aabeabcaa DOES NOT match (R2) (?^:^((a*)[bd]|e?(a*)b)*c\2$)
test 7: (S3) aababcaa DOES NOT match (R2) (?^:^((a*)[bd]|e?(a*)b)*c\2$)

请注意,egrep(或无论如何,GNU egrep)认为上述每项测试都是匹配。

我认为这在理论上是正确的&#34;回答如果regexp析取被解释为非确定性选择,那就是存在可以使匹配成功的替代选择。

另请注意,(S2S3R1取代b d S0 {1}},S1),这是认为第四次测试应该匹配的另一个原因。

直观地说,我也希望测试4-7是匹配,只要测试0-3是。

我可以理解一个人如何达到不匹配的第四个测试:通过在每个分离时按此顺序尝试左分支和右分支,如果回溯没有正确恢复 {{1变量到它的先前值,在S3的后一个R0子串上探索R1析取的左分支会破坏\2ab,然后它不会回溯到它的{{} 1}}值,导致匹配失败(而在之前的任何测试中都不会发生同样的事情)。

但我不知道我的分析是否正确。为什么第五次测试不匹配真的让我失望。

所以无论如何,我的问题是以下各项的组合:

  • 有人可以详细解释Perl的regexp引擎对这些示例的行为吗?

  • 这种行为是故意的吗?是否记录在某处?

  • 我应该提交错误吗?

3 个答案:

答案 0 :(得分:3)

egrep和Perl之间有一个更简单的例子:

grep -iE '^(([ab])|([ab]))*\2$' <<< abA
abA
perl -wE 'say for shift =~ /^(([ab])|([ab]))*\2$/i' abA

有趣的是,Perl中的以下匹配(以及egrep):

grep -iE '^(([ab])|([ab]))*(\3)$' <<< abA
abA
perl -wE 'say for shift =~ /^(([ab])|([ab]))*(\3)$/i' abA
b
b
a
A

因此,第一个a*的第一次迭代匹配,b与第二次匹配(因为\1 eq 'b')。与此同时,\3 eq 'a',但\4 eq 'A'。为什么\3 eq 'a'?它似乎是前一次*迭代的结果,我说这是一个错误。

更新:我报告了一个错误。

答案 1 :(得分:1)

让我们来看看第四个例子。 (请不要从零开始编号!我是人,而不是电脑!)

vvXvXcvv

不匹配

qr/ ^ (
    (v*) X
    |
    H? (v*) X
)* c \2 $ /x
  • 在字符串的开头,perl匹配两个备选项中的第一个。 vvX匹配(v*) X,因此无需尝试替代方案。这也将捕获2保存为vv

    使vXcvv引擎匹配

  • 同样,perl使用vX来匹配(v*) X。它将捕获2保存为v,并且引擎会再次尝试

    离开cvv

  • 剩下的唯一选项是( (v*) X | H? (v*) X )*的另一次迭代,或者从c \2

  • 中退出该循环
  • 该文字不以vXH开头,因此循环结束,下一个匹配为c \2,正则表达式引擎与c

    匹配

    现在只有vv匹配

  • perl现在正在寻找一个匹配来捕获2,即v。成功

    剩下的字符串只是v

  • 现在perl正在寻找$,它是字符串的结尾,或者只是在字符串末尾的换行符之前。它看到v,因此失败

我真的希望有所帮助。我并不急于解释剩下的四个例子,但我还不明白为什么会有混淆

我还没有尝试过egrep,我感到惊讶的是它表现得与众不同。也许它不像Perl那样堆叠捕获?

如果有任何进一步的兴趣,请告诉我

答案 2 :(得分:0)

以下是我对行为的理解:

test 3: (S3) aababcaa DOES NOT match (R1) (?^:^((a*)b|e?(a*)b)*c\2$)

备选方案的第一部分在这里失败,然后我们使用第二部分。

第2组包含a所以使用反向引用正则表达式与:

 ^(e?(a*)b)*ca$

这与最后有aababcaa的字符串aa不匹配。

如果中间有aaaabaabcaa,则匹配正常:ffmpeg -i src2 -itsoffset 20 -i src1 -c copy -map 0:v -map 1:a new.mp4