[英]I would like explanation of the behaviour of Perl's regular expression engine
我已經將此代碼重寫為我認為更易於理解的代碼。 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;
}
"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
)都認為上面的每個測試都是匹配項。
我認為,如果將正則表達式析取解釋為非確定性選擇,則從理論上講是“正確的”答案,從某種意義上說,存在選擇將使匹配成功的選擇。
還要注意,通過在( S0
, S1
, R0
)中的各處將b
替換為d
來獲得( S2
, S3
, R1
),這是認為第四次測試應該匹配的另一個原因。
憑直覺,我還希望測試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
文本不是以v
, X
或H
開頭的,因此循環結束,下一個匹配項是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.