鍍金池/ 教程/ C/ 平衡組/遞歸匹配
后向引用
零寬斷言
如何寫(xiě)出高效率的正則表達(dá)式
平衡組/遞歸匹配
字符轉(zhuǎn)義
元字符
測(cè)試正則表達(dá)式
什么是正則表達(dá)式
重復(fù)
貪婪與懶惰
字符類(lèi)
常用正則表達(dá)式
詳細(xì)語(yǔ)法
表達(dá)式全集
處理選項(xiàng)
負(fù)向零寬斷言
反義
分枝條件
注釋
分組
入門(mén)

平衡組/遞歸匹配

有時(shí)我們需要匹配像( 100 * ( 50 + 15 ) )這樣的可嵌套的層次性結(jié)構(gòu),這時(shí)簡(jiǎn)單地使用\(.+\)則只會(huì)匹配到最左邊的左括號(hào)和最右邊的右括號(hào)之間的內(nèi)容(這里我們討論的是貪婪模式,懶惰模式也有下面的問(wèn)題)。假如原來(lái)的字符串里的左括號(hào)和右括號(hào)出現(xiàn)的次數(shù)不相等,比如( 5 / ( 3 + 2 ) ) ),那我們的匹配結(jié)果里兩者的個(gè)數(shù)也不會(huì)相等。有沒(méi)有辦法在這樣的字符串里匹配到最長(zhǎng)的,配對(duì)的括號(hào)之間的內(nèi)容呢?

為了避免(\(把你的大腦徹底搞糊涂,我們還是用尖括號(hào)代替圓括號(hào)吧?,F(xiàn)在我們的問(wèn)題變成了如何把xx <aa aa> yy這樣的字符串里,最長(zhǎng)的配對(duì)的尖括號(hào)內(nèi)的內(nèi)容捕獲出來(lái)?

這里需要用到以下的語(yǔ)法構(gòu)造:

  • (?'group') 把捕獲的內(nèi)容命名為 group,并壓入堆棧(Stack)
  • (?'-group') 從堆棧上彈出最后壓入堆棧的名為 group 的捕獲內(nèi)容,如果堆棧本來(lái)為空,則本分組的匹配失敗
  • (?(group)yes|no) 如果堆棧上存在以名為 group 的捕獲內(nèi)容的話(huà),繼續(xù)匹配 yes 部分的表達(dá)式,否則繼續(xù)匹配no部分
  • (?!) 零寬負(fù)向先行斷言,由于沒(méi)有后綴表達(dá)式,試圖匹配總是失敗

我們需要做的是每碰到了左括號(hào),就在壓入一個(gè)"Open",每碰到一個(gè)右括號(hào),就彈出一個(gè),到了最后就看看堆棧是否為空--如果不為空那就證明左括號(hào)比右括號(hào)多,那匹配就應(yīng)該失敗。正則表達(dá)式引擎會(huì)進(jìn)行回溯(放棄最前面或最后面的一些字符),盡量使整個(gè)表達(dá)式得到匹配。

<                         #最外層的左括號(hào)
    [^<>]*                #最外層的左括號(hào)后面的不是括號(hào)的內(nèi)容
    (
        (
            (?'Open'<)    #碰到了左括號(hào),在黑板上寫(xiě)一個(gè)"Open"
            [^<>]*       #匹配左括號(hào)后面的不是括號(hào)的內(nèi)容
        )+
        (
            (?'-Open'>)   #碰到了右括號(hào),擦掉一個(gè)"Open"
            [^<>]*        #匹配右括號(hào)后面不是括號(hào)的內(nèi)容
        )+
    )*
    (?(Open)(?!))         #在遇到最外層的右括號(hào)前面,判斷黑板上還有沒(méi)有沒(méi)擦掉的"Open";如果還有,則匹配失敗

>                         #最外層的右括號(hào)

平衡組的一個(gè)最常見(jiàn)的應(yīng)用就是匹配 HTML,下面這個(gè)例子可以匹配嵌套的

標(biāo)簽:<div[^>]*>[^<>]*(((?'Open'<div[^>]*>)[^<>]*)+((?'-Open'</div>)[^<>]*)+)*(?(Open)(?!))</div>。