我想解释Perl的正则表达式引擎的行为

Gro*_*sen 8 regex perl

由@Borodin更新

我已经将这段代码重写为我认为更易于理解的内容.OP正在b与之比较d等等,我已经将所有符号更改为更加不同的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;
}
Run Code Online (Sandbox Code Playgroud)

产量

"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 $ )
Run Code Online (Sandbox Code Playgroud)



以下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";
}
Run Code Online (Sandbox Code Playgroud)

产量

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$)
Run Code Online (Sandbox Code Playgroud)

请注意egrep(或者无论如何,GNU egrep)认为上面的每个测试都是匹配的.

我认为这是理论上"正确"的答案,如果正则表达式分离被解释为非确定性选择,在某种意义上存在可以使匹配成功的替代选择.

还要注意的是(S2,S3,R1)通过替换获得bd在(无处不在S0,S1,R0),这是另一个原因认为第四测试应该是匹配的.

直觉上,我还希望测试4-7是匹配,只要测试0-3是.

我有点明白怎么一会到达第四个测试不匹配:通过尝试左分支,并在每个脱节这个定单的权利右支,如果回溯不正确还原\2变量,它的前值,探索左支对后者的R1脱节的abS3的子会揍\2a这会再不能回溯到它的aa价值,导致比赛失败(而同样的事情不会在任何先前测试的发生).

但我不知道我的分析是否正确.为什么第五次测试不匹配真的逃脱了我.

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

  • 有人可以详细解释Perl的regexp引擎行为吗?

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

  • 我应该提交错误吗?

Tot*_*oto 0

这是我对这种行为的理解:

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

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

第 2 组包含a,因此使用反向引用,正则表达式与以下内容相同:

 ^(e?(a*)b)*ca$
Run Code Online (Sandbox Code Playgroud)

这与末尾aababcaa的字符串不匹配。aa

aa如果中间有双,那么比赛就可以了:aabaabcaa