簡體   English   中英

我想解釋一下Perl正則表達式引擎的行為

[英]I would like explanation of the behaviour of Perl's regular expression engine

@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 )都認為上面的每個測試都是匹配項。

我認為,如果將正則表達式析取解釋為非確定性選擇,則從理論上講是“正確的”答案,從某種意義上說,存在選擇將使匹配成功的選擇。

還要注意,通過在( S0S1R0 )中的各處將b替換為d來獲得( S2S3R1 ),這是認為第四次測試應該匹配的另一個原因。

憑直覺,我還希望測試4–7與測試0–3匹配。

但是我不知道我的分析是否正確。 為什么第五次考試不匹配真的讓我感到困惑。

所以無論如何,我的問題是以下各項的組合:

  • 有人可以在這些示例中詳細解釋Perl的正則表達式引擎行為嗎?

  • 這種行為是故意的嗎? 是否記錄在某處?

  • 我應該提交錯誤嗎?

關於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' 這似乎是*的先前迭代的結果,我認為這是一個錯誤。

更新:我報告了一個錯誤。

讓我們來看第四個例子。 (請不要從零開始編號!我是人,不是計算機!)

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那樣堆疊捕獲內容?

請讓我知道是否還有其他興趣

這是我對行為的理解:

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

替代方案的第一部分在這里失敗,然后我們使用第二部分。

第2組包含a因此使用反向引用,正則表達式與:

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

該字符串與結尾為aa的字符串aababcaa不匹配。

如果中間有一個雙aa ,那么匹配就可以了: aabaabcaa

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM