2014-10-26 161 views
1

我試圖使用遞歸正則表達式來匹配類似bash的變量擴展。基本上,我應該能夠匹配類似下面的字符串:正則表達式遞歸:匹配兩個字符的開頭

${FOO=BAR} 
${FOO=${BAR=BAZ}} 

而且處理的投入是這樣的:

${FOO=\${BAR=BAZ}} 
${FOO={${BAR=BAZ}} 

在第一種情況下,應該投其所好,但不包括最後} ,第二種情況應該完全匹配。這是因爲我試圖將兩個字符的開頭${與單字符關閉}匹配。開幕式和閉幕式都應該能夠逃脫。

我已經得到遠至PCRE正則表達式\$\{(?:[^{}]|(?R))*\}。但是這不能正確處理轉義。如果我將其修改爲(?:^|[^\\])(?:\\\\)*(\$\{(?:[^{}]|(?R))*\}),那麼只有最外層的轉義匹配正確。

這可以用正則表達式來完成,還是我最好只寫一個pyparsing解析器?

+0

你試圖匹配的東西有點不清楚,特別是最後兩個例子。爲什麼最後一個例子沒有平衡大括號? – 2014-10-26 21:21:52

+0

第一個示例轉義了兩個字符的開頭序列:我試圖將'$ {'與'}'匹配,而不僅僅是{{和}}。在第一種情況下,整個匹配應該是'$ {FOO = \ $ {BAR = BAZ}',而第二種情況下整個字符串應該匹配,因爲第二個'{'實際上並沒有打開任何東西。 – 2014-10-26 21:23:33

+0

我編輯了這個問題,使其更清晰。 – 2014-10-26 21:24:52

回答

3

你可以試試這個模式:

(?s)\\.(*SKIP)(*F)|(?s)(\${(?>[^$}\\]+|\\.|(?1))*}) 

online example

細節:

(?s) 
\\.    # an escaped character 
(*SKIP)   # skip the matched content if the pattern fails later 
(*F)    # force the pattern to fail 
| 
(?s) 
(
    \${ 
    (?>   # open a atomic group 
     [^$}\\]+ # all that is not a backslash, a $ or a } 
     |   # OR 
     \\.  # an escaped character 
     |   # OR 
     (?1)  # recurse to group 1 
    )*   # repeat the atomic group zero or more times 
    }    
) 

主要的想法是爲了避免一個逃脫的美元,然後左大括號被視爲一個開放標籤。

備註:而不是使用每個分支的內聯修飾符(?s),您可以刪除它們並使用全局修飾符s

要完全嚴格,您可以在原子組中添加替代\$(?!{),以允許$之後的內容中沒有開頭的大括號。 (前遞歸)

關於(*SKIP)(*FAIL)

(*SKIP)(*FAIL)被稱爲回溯控制動詞

當模式失敗時,NFA正則表達式引擎的默認行爲是使用回溯機制。這些動詞可以控制這種機制。

更具體地說,組合subpattern(*SKIP)(*FAIL)的目標是從匹配結果中排除子模式匹配的內容,並禁止正則表達式引擎用匹配的子字符串嘗試任何其他內容。子字符串被跳過。

你可以看到一個full explanation here.

關於原子團:

一個atomic group是一個非捕獲組。唯一的區別是一旦左括號達到,正則表達式引擎就不允許在括號內匹配的字符之間回溯。它只能到組前的位置。原子組使得匹配的子字符串不可分割(原子)。

這裏,原子組阻止catastrophic backtracking,如果模式後來失敗,可能會出現這種結構(?:A+|B)+

+0

哇,那很整齊!什麼,確切地說,是一個原子團體?我也不知道'(* SKIP)'或'(* FAIL)'。你能解釋一下嗎? – 2014-10-26 21:40:33