這個正則表達式匹配迴文: ^((.)(?1)\2|.?)$
正則表達式引擎如何用遞歸子模式解析正則表達式?
無法圍繞它如何工作。 什麼時候遞歸結束,當正則表達式從遞歸子模式中斷開並轉到"|.?"
部分?
謝謝。
編輯:對不起,我沒有解釋\2
和(?1)
(?1)
- 指第一個子模式(自身)
\2
- 向後引用的匹配第二子模式,這是(.)
上面的例子用PHP編寫。同時匹配「ABBA」(無中旬迴文字符)和「ABCBA」 - 有一箇中間的非反射特性
這個正則表達式匹配迴文: ^((.)(?1)\2|.?)$
正則表達式引擎如何用遞歸子模式解析正則表達式?
無法圍繞它如何工作。 什麼時候遞歸結束,當正則表達式從遞歸子模式中斷開並轉到"|.?"
部分?
謝謝。
編輯:對不起,我沒有解釋\2
和(?1)
(?1)
- 指第一個子模式(自身)
\2
- 向後引用的匹配第二子模式,這是(.)
上面的例子用PHP編寫。同時匹配「ABBA」(無中旬迴文字符)和「ABCBA」 - 有一箇中間的非反射特性
^((.)(?1)\2|.?)$
的^
和$
斷言開頭和字符串的分別結束。讓我們來看看兩者之間的內容,這是更有趣:
((.)(?1)\2|.?)
1------------1 // Capturing group 1
2-2 // Capturing group 2
看第一部分(.)(?1)\2
,我們可以看到,它會嘗試匹配任何字符,並在最後的是相同的字符(反向引用\2
,它是指與(.)
匹配的字符)。在中間,它將遞歸匹配整個捕獲組1.注意,有一個隱式斷言(由(.)
匹配一個字符在開始和\2
匹配相同的字符在末尾),要求字符串至少是至少2個字符。第一部分的目的是遞歸地切斷字符串的相同的末端。
看第二部分.?
,我們可以看到它會匹配一個或0個字符。如果字符串最初長度爲0或1,或者遞歸匹配中的剩餘字符爲0或1個字符後,纔會匹配。第二部分的目的是在兩端截斷字符串後匹配空字符串或單個孤立字符。
遞歸匹配的工作原理:
^
和$
斷言。我們無法從任何隨機位置開始匹配。正則表達式基本上等同於下面的僞代碼:
palin(str) {
if (length(str) >= 2) {
first = str[0];
last = str[length(str)-1];
return first == last && palin(substr(str, 1, length(str)-2));
} else
// empty and single-char trivially palindromes
return true;
}
我還沒有發現PCRE正則表達式的任何漂亮的調試工具。我越能找到的是如何傾倒字節碼:
$ pcretest -b
PCRE version 7.6 2008-01-28
re> /^((.)(?1)\2|.?)$/x
------------------------------------------------------------------
0 39 Bra
3 ^
4 26 CBra 1
9 6 CBra 2
14 Any
15 6 Ket
18 6 Once
21 4 Recurse
24 6 Ket
27 \2
30 5 Alt
33 Any?
35 31 Ket
38 $
39 39 Ket
42 End
------------------------------------------------------------------
Perl有更好的調試工具比PCRE,嘗試echo 123454321 | perl -Mre=debug -ne '/^((.)(?1)\2|.?)$/x'
。這不僅給一些字節碼,類似於PCRE的一個,但它也顯示了每個步驟和消耗,在每一步的剩餘部分輸入:
Compiling REx "^((.)(?1)\2|.?)$"
Final program:
1: BOL (2)
2: OPEN1 (4)
4: BRANCH (15)
5: OPEN2 (7)
7: REG_ANY (8)
8: CLOSE2 (10)
10: GOSUB1[-8] (13)
13: REF2 (19)
15: BRANCH (FAIL)
16: CURLY {0,1} (19)
18: REG_ANY (0)
19: CLOSE1 (21)
21: EOL (22)
22: END (0)
floating ""$ at 0..2147483647 (checking floating) anchored(BOL) minlen 0
Guessing start of match in sv for REx "^((.)(?1)\2|.?)$" against "12321"
Found floating substr ""$ at offset 5...
Guessed: match at offset 0
Matching REx "^((.)(?1)\2|.?)$" against "12321"
0 <> <12321> | 1:BOL(2)
0 <> <12321> | 2:OPEN1(4)
0 <> <12321> | 4:BRANCH(15)
0 <> <12321> | 5: OPEN2(7)
0 <> <12321> | 7: REG_ANY(8)
1 <1> <2321> | 8: CLOSE2(10)
1 <1> <2321> | 10: GOSUB1[-8](13)
1 <1> <2321> | 2: OPEN1(4)
1 <1> <2321> | 4: BRANCH(15)
1 <1> <2321> | 5: OPEN2(7)
1 <1> <2321> | 7: REG_ANY(8)
2 <12> <321> | 8: CLOSE2(10)
2 <12> <321> | 10: GOSUB1[-8](13)
2 <12> <321> | 2: OPEN1(4)
2 <12> <321> | 4: BRANCH(15)
2 <12> <321> | 5: OPEN2(7)
2 <12> <321> | 7: REG_ANY(8)
3 <123> <21> | 8: CLOSE2(10)
3 <123> <21> | 10: GOSUB1[-8](13)
3 <123> <21> | 2: OPEN1(4)
3 <123> <21> | 4: BRANCH(15)
3 <123> <21> | 5: OPEN2(7)
3 <123> <21> | 7: REG_ANY(8)
4 <1232> <1> | 8: CLOSE2(10)
4 <1232> <1> | 10: GOSUB1[-8](13)
4 <1232> <1> | 2: OPEN1(4)
4 <1232> <1> | 4: BRANCH(15)
4 <1232> <1> | 5: OPEN2(7)
4 <1232> <1> | 7: REG_ANY(8)
5 <12321> <> | 8: CLOSE2(10)
5 <12321> <> | 10: GOSUB1[-8](13)
5 <12321> <> | 2: OPEN1(4)
5 <12321> <> | 4: BRANCH(15)
5 <12321> <> | 5: OPEN2(7)
5 <12321> <> | 7: REG_ANY(8)
failed...
5 <12321> <> | 15: BRANCH(19)
5 <12321> <> | 16: CURLY {0,1}(19)
REG_ANY can match 0 times out of 1...
5 <12321> <> | 19: CLOSE1(21)
EVAL trying tail ... 9d86dd8
5 <12321> <> | 13: REF2(19)
failed...
failed...
BRANCH failed...
4 <1232> <1> | 15: BRANCH(19)
4 <1232> <1> | 16: CURLY {0,1}(19)
REG_ANY can match 1 times out of 1...
5 <12321> <> | 19: CLOSE1(21)
EVAL trying tail ... 9d86d70
5 <12321> <> | 13: REF2(19)
failed...
4 <1232> <1> | 19: CLOSE1(21)
EVAL trying tail ... 9d86d70
4 <1232> <1> | 13: REF2(19)
failed...
failed...
BRANCH failed...
3 <123> <21> | 15: BRANCH(19)
3 <123> <21> | 16: CURLY {0,1}(19)
REG_ANY can match 1 times out of 1...
4 <1232> <1> | 19: CLOSE1(21)
EVAL trying tail ... 9d86d08
4 <1232> <1> | 13: REF2(19)
failed...
3 <123> <21> | 19: CLOSE1(21)
EVAL trying tail ... 9d86d08
3 <123> <21> | 13: REF2(19)
failed...
failed...
BRANCH failed...
2 <12> <321> | 15: BRANCH(19)
2 <12> <321> | 16: CURLY {0,1}(19)
REG_ANY can match 1 times out of 1...
3 <123> <21> | 19: CLOSE1(21)
EVAL trying tail ... 9d86ca0
3 <123> <21> | 13: REF2(19)
4 <1232> <1> | 19: CLOSE1(21)
EVAL trying tail ... 0
4 <1232> <1> | 13: REF2(19)
5 <12321> <> | 19: CLOSE1(21)
5 <12321> <> | 21: EOL(22)
5 <12321> <> | 22: END(0)
Match successful!
Freeing REx: "^((.)(?1)\2|.?)$"
正如你可以看到,Perl中首先消耗所有輸入遞歸直到(.)
失敗,然後開始回溯並且嘗試從交替.?
和第一部分\2
的其餘部分,當它失敗時回溯,直到它最終成功爲止。
爲什麼在這個調試中,有六個'OPEN1',但相應的八個'CLOSE1'?它是否也不是六個'CLOSE1'? – revo 2016-06-27 21:59:11
這可能會在程序員更好,因爲這真的不是一個「實際」的問題。 – Jeff 2012-07-26 15:10:10
'。?'可能用於奇數長度的字符串。 – nhahtdh 2012-07-26 15:11:44
對不起,但我可以知道'(?1)'和'\ 2'是什麼? – 2012-07-26 15:23:24