2012-07-26 98 views
5

這個正則表達式匹配迴文: ^((.)(?1)\2|.?)$正則表達式引擎如何用遞歸子模式解析正則表達式?

無法圍繞它如何工作。 什麼時候遞歸結束,當正則表達式從遞歸子模式中斷開並轉到"|.?"部分?

謝謝。

編輯:對不起,我沒有解釋\2(?1)

(?1) - 指第一個子模式(自身)

\2 - 向後引用的匹配第二子模式,這是(.)

上面的例子用PHP編寫。同時匹配「ABBA」(無中旬迴文字符)和「ABCBA」 - 有一箇中間的非反射特性

+0

這可能會在程序員更好,因爲這真的不是一個「實際」的問題。 – Jeff 2012-07-26 15:10:10

+0

'。?'可能用於奇數長度的字符串。 – nhahtdh 2012-07-26 15:11:44

+0

對不起,但我可以知道'(?1)'和'\ 2'是什麼? – 2012-07-26 15:23:24

回答

3

^((.)(?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個字符後,纔會匹配。第二部分的目的是在兩端截斷字符串後匹配空字符串或單個孤立字符。

遞歸匹配的工作原理:

  • 整個字符串必須迴文傳,由^$斷言。我們無法從任何隨機位置開始匹配。
  • 如果字符串是< = 1個字符,它會通過。
  • 如果字符串大於2個字符,它是否被接受僅由第一部分決定。如果匹配,它將被切斷2個末端。
  • 剩餘的如果匹配,只能用2個末端截斷,或者如果長度爲< = 1,則通過。
+0

感謝您的回覆。我用更多的解釋編輯了原文。 '\ 2'不是長度。 – alexy2k 2012-07-26 15:59:51

+1

'\ 2'不是長度,但它強制該表達式的長度至少爲兩個字符,因爲單個字符不能同時匹配'(。)'和反向引用'\ 2'。 – 2012-07-26 16:06:20

+2

由於第一部分它每次都會遞歸,它將處理刪除了兩個末尾字符的字符串。最終縮小到0或1個字符,然後第二部分匹配,遞歸停止。 – Barmar 2012-07-26 16:19:46

3

正則表達式基本上等同於下面的僞代碼:

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; 
} 
1

我還沒有發現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的其餘部分,當它失敗時回溯,直到它最終成功爲止。

+0

爲什麼在這個調試中,有六個'OPEN1',但相應的八個'CLOSE1'?它是否也不是六個'CLOSE1'? – revo 2016-06-27 21:59:11