2015-01-07 50 views
2

TL; DR:有沒有辦法指定一個條件,以便開始元素必須匹配其配對關閉元素?條件可以用來配對平衡組元素嗎?

示例位於on regex101.com

=====

在正則表達式平衡元件通常是通過遞歸處理。這意味着可以找到嵌套的{...{...{...}...}...}

此外,PCRE允許(?(DEFINE)...)結構,它允許您定義各種模式而不實際開始匹配。

在常規表達式

# Define the opening and closing elements before the recursion occurs 
(?(DEFINE) 
    (?<open_curly>\{) 
    (?<close_curly>\}) 
    # ... other definitions here ... 

    (?<open>\g'open_curly') 
    (?<close>\g'close_curly') 
) 

# Match the opening element 
(\g'open' 
    (?> 
    # For recursion, don't match either the opening or closing element 
    (?!\g'open'|\g'close')(?s:.) 
    | 
    # Recurse this captured pattern 
    (?-1) 
)* 

# Match the closing element 
\g'close') 

的元素是{}字符,並且可以匹配針對圖案,例如

{{{}}} 
{ test1 { test2 { test3 { test4 } } } } 

我想包括其他開/關元件,諸如[]--[--],因此包括在(?(DEFINE))中的那些:

(?<open_square>\[) 
(?<close_square>\]) 
(?P<open_pascal>(?i:\bbegin\b)) 
(?P<close_pascal>(?i:\bend\b)) 
(?P<open_lua>--\[) 
(?P<close_lua>--\]) 
(?<open>\g'open_curly'|\g'open_square'|\g'open_pascal'|\g'open_lua') 
(?<close>\g'close_curly'|\g'close_square'|\g'close_pascal'|\g'close_lua') 

什麼這並不正確地做的是與它的閉合元件對的開口元件,允許向--[},這是不期望的。

有沒有辦法在這樣的正則表達式中創建開放/關閉對?

+0

在一般的情況下,正則表達式的不能檢測平衡元件,事業正則表達式是一個狀態機,但你需要一個堆棧機。 –

+0

@Mark模式匹配庫允許您使用遞歸(需要堆棧)來允許匹配的嵌套結構。看到我的答案。一些實現(.NET)使用平衡組,這也是一個堆棧。所以你肯定可以將嵌套結構與這些模式相匹配 - 只有你不能再稱它們爲* regular *。 –

+0

謝謝。這是我的一個新信息。 –

回答

1

我會說沒有用污染的邏輯與一堆命名組和
不必要的遞歸是不可預知的。

維護正確的遞歸有三個主要部分(下面列出)。
要做到這一點,你必須解析一切,所以你必須考慮
內容和不平衡的錯誤。

引擎不會讓你捕捉到細節,任何超出第一級的東西。
這意味着它更容易地維護TOP,您可以從拉動信息和CORE
,你不能保持,但不會遞歸。幾個細微的差異,但在下面的例子中可以看到

每當一個被遞歸,則立刻由唯一的分隔符的個體
組(一對)所包圍。這是爲了正確地展開堆疊。
這個過程不能被分解出來(一般化)。

更新
一般此正則表達式被稱爲遞歸函數內,每次經過CORE到它。
示例僞代碼:

bool bIsOk = true; 
bool RecurseCore(string core) 
{ 
    while(regex_search (core, regex, match)) 
    { 
      if (match[1].success) { print 'content' } 
      else 
      if (match[2].success) { print 'square'; bIsOk = RecurseCore(match[2].value) } 
      else 
      if (match[3].success) { print 'curly'; bIsOk = RecurseCore(match[3].value) } 
      else 
      if (match[4].success) { print 'pascal'; bIsOk = RecurseCore(match[4].value) } 
      else 
      if (match[5].success) { print 'lua'; bIsOk = RecurseCore(match[5].value) } 
      else 
      if (match[6].success) { print 'error'; bIsOk = false } // error 
      if (bIsOk == false) { break } 
    } 
    return bIsOk; 
} 

正則表達式:

# ////////////////////////////////////////////////////// 
# // The General Guide to 3-Part Recursive Parsing 
# // ---------------------------------------------- 
# // Part 1. CONTENT 
# // Part 2. CORE 
# // Part 3. ERRORS 

(?si)      # Dot all, no case 

(?: 
     (       # (1), Take off CONTENT 
      (?&content) 
    ) 
    |       # OR 
     \[       # Square's delimiter 
     (       # (2), square CORE 
      (?= .) 
      (?&core) 
     | 
    ) 
     \]       # End-Delimiter 
    |       # OR 
     \{       # Curly's delimiter 
     (       # (3), curly CORE 
      (?= .) 
      (?&core) 
     | 
    ) 
     \}       # End-Delimiter 
    |       # OR 
     \b begin \b    # Pascal's delimiter 
     (       # (4), pascal CORE 
      (?= .) 
      (?&core) 
     | 
    ) 
     \b end \b     # End-Delimiter 
    |       # OR 
     --\[      # Lua's delimiter 
     (       # (5), lua CORE 
      (?= .) 
      (?&core) 
     | 
    ) 
     --\]      # End-Delimiter 
    |       # OR 
     (       # (6), Take off Unbalanced (delimeter) ERRORS 
      \b 
      (?: begin | end) 
      \b 
     | -- [\[\]] 
     | [\[\]{}] 
    ) 
) 

# /////////////////////// 
# // Subroutines 
# // --------------- 

(?(DEFINE) 

     # core 
     (?<core> 
      (?> 
       (?&content) 
      | 
       \[       
       (?:      # Square delimiter 
        (?= .)     # recurse core 
        (?&core)     
        | 
       ) 
       \] 
      |       # OR 
       \{ 
       (?:      # Curly delimiter 
        (?= .)     # recurse core 
        (?&core) 
        | 
       ) 
       \}  
      |       # OR 
       \b begin \b 
       (?:      # Pascal delimiter 
        (?= .)     # recurse core 
        (?&core) 
        | 
       ) 
       \b end \b  
      |       # OR 
       --\[ 
       (?:      # Lua delimiter 
        (?= .)     # recurse core 
        (?&core) 
        | 
       ) 
       --\]  
      )+ 
    ) 

     # content 
     (?<content> 
      (?> 
       (?! 
        \b 
        (?: begin | end) 
        \b 
        | -- [\[\]] 
        | [\[\]{}] 
       ) 
       . 
      )+ 
    ) 

) 
2

你說錯了。你可以用遞歸實現這樣的:

(?(DEFINE) 
    (?<curly> \{  \g<content>*? \}  ) 
    (?<square> \[  \g<content>*? \]  ) 
    (?<pascal> \bbegin\b \g<content>*? \bend\b) 
    (?<lua> --\[  \g<content>*? --\] ) 

    (?<nested> \g<curly> | \g<square> | \g<pascal> | \g<lua>) 

    (?<content> 
    # Math non-recursive content (atomically) 
    (?: (?! [{}\[\]] | \bbegin\b | \bend\b | --[\[\]]) .)++ 
    # Or recurse 
    | \g<nested> 
) 
) 

\g<nested> 

Demo

這裏的技巧是利用正則表達式引擎棧來的內存匹配打開符號排序,以要求當正則表達式引擎在離開遞歸時展開堆棧時匹配關閉符號。您還需要確保全部收集.不會消耗組開始/結束符號。


簡單的情況是更易讀:

(?(DEFINE) 
    (?<curly> \{ \g<content>*? \}) 
    (?<square> \[ \g<content>*? \]) 

    (?<nested> \g<curly> | \g<square>) 

    (?<content> [^{}\[\]]++ | \g<nested>) 
) 

\g<nested> 

Demo

通知的原子團。它可以防止過度的回溯。此外,這個正則表達式會立即消耗任何不能遞歸的東西,而不會首先嚐試遞歸。


此外,要回答你的問題,條件在這裏沒有用,因爲你需要一個堆棧來跟蹤你需要匹配的結尾符號。

某些實現(PCRE)允許您通過遞歸模式使用堆棧,而其他實現(.NET)通過balancing groups公開堆棧。但是當你有幾種平衡結構時,遞歸是明顯的贏家。

+0

我很喜歡這樣一個事實,即沒有明確的遞歸調用,但它基於相互引用的組進行遞歸。我同意,單字符元素*更優化,因爲它們不需要回溯。我很好奇爲什麼'(? ...)'行需要重新指定要被否定的元素,而不是重複使用'\ g '樣式構造。 – OnlineCop

+0

@OnlineCop事情是......'\ g' **是一個明確的遞歸,只是在這種情況下它是一個相互遞歸:)這就是爲什麼你不能重新使用'\ g'來否定內部任何東西'內容' - 它會緩解。另外,我以可讀性爲代價優化了第一種情況。 –

相關問題