2014-12-19 64 views
1

我目前正試圖在PHP中編寫一個正則表達式,該正則表達式允許我匹配包含自身的特定模式的完全嵌套。我知道每個默認的正則表達式都無法做到這一點,但PHP的遞歸模式(http://php.net/manual/de/regexp.reference.recursive.php)應該可以。使用正則表達式(使用PHP的遞歸)匹配嵌套模式

我有嵌套的結構是這樣的:

<a=5> 
    <a=3> 
     Foo 
     <b>Bar</b> 
    </a> 
    Baz 
</a> 

現在我想最外層標籤的內容相匹配。爲了正確匹配第一個開始標籤和最後一個結束標籤,我需要PHP的遞歸項(?R)

我試圖像這樣的圖案:

/<a=5>((?R)|[^<]|<\/?[^a]|<\/?a[a-zA-Z0-9-])*<\/a>/s 

這基本上意味着<a=5>,隨後儘可能多的下面,接着</a>

  • 另一個標籤(遞歸的)
  • 任何非開始標記字符
  • 任何開始標記,後跟一個可選斜線,後面跟着一個「a」
  • 在之前的一個,但沒有完成(後面至少1個字元)

最後2情況都只是一個案例[標籤不namend「一」],但我聽說這應該在正則表達式中應避免使用,因爲它需要lookarounds並且性能不好。

但是,我在RegEx中看到沒有錯誤,但它與給定的字符串不匹配。我想下面的比賽:

<a=3> 
     Foo 
     <b>Bar</b> 
    </a> 
    Baz 

下面就玩的正則表達式的鏈接:https://www.regex101.com/r/lO1wA6/1

+1

如果你真的只爲開'<'標誌,那麼它可能確實是足夠了。關於'(?R)'的一個注意:它不會緩解第一組,而是整個模式。使用'R1'。但是,仍然可以嘗試使用'/ x'來實現可讀性和內聯註釋,並且還提供了匹配成功的更基本示例。 – mario 2014-12-19 01:56:00

+0

謝謝,我剛剛意識到我的錯誤。我希望外部標籤匹配'a = 5',但內部匹配'a。*?'。 – Felk 2014-12-19 02:00:15

+1

這看起來像xml/html。你應該使用xml/html解析器來做到這一點。 – pguardiario 2014-12-19 02:33:51

回答

2

您可以使用此正則表達式來匹配你想要什麼(放置在一個字符串字面爲方便起見,正則表達式):

'~<a=5>(<([a-zA-Z0-9]+)[^>]*>(?1)*</\2>|[^<>]++)*</a>~' 

這裏是正則表達式上方的向下突破:

<a=5> 
(
    <([a-zA-Z0-9]+)[^>]*> 
    (?1)* 
    </\2> 
    | 
    [^<>]++ 
)* 
</a> 

第一部分<([a-zA-Z0-9]+)[^>]*>(?1)*</\2>比賽對匹配標籤和它的所有內容。它假定標籤的名稱由字符[a-zA-Z0-9]組成。當匹配結束標記</\2>時,標記的名稱被捕獲([a-zA-Z0-9]+)和反向引用。

第二部分[^<>]++與標籤外的任何其他部分匹配。請注意,沒有處理引用的字符串,因此取決於您的輸入,它可能無法正常工作。

然後返回遞歸調用第一個捕獲組的例程調用。您會注意到標籤可以包含0個或更多其他標籤或非標籤內容的實例。由於正則表達式的寫法,該屬性也由最外層的<a=5>...</a>對共享。

Demo on regex101

+0

謝謝,這是我希望它工作的方式。我只需要將外部'(...)*'封裝在另一個組中,並修改組計數器即可將最外面的標記作爲匹配項。 – Felk 2014-12-19 19:00:34

-1

試試這個:

PHP

$re = "/(<[^\\/>]+(\\/?)>)*([^<]+)(<\\/\\w+>)*/m"; 
$str = "<a=5>\n <a=3>\n Foo\n <b/>Bar</b>\n </a>\n Baz\n</a>"; 

preg_match_all($re, $str, $matches); 
var_dump($matches); 
// here 

$matches[1]; //for open tag array 
$matches[2]; //for single tag mark array by (/>) 
$matches[3]; //for inner data array 
$matches[4]; //for close tag array 

輸出

array (size=5) 
    0 => 
    array (size=5) 
     0 => string '<a=5> 
' (length=7) 
     1 => string '<a=3> 
Foo 
' (length=12) 
     2 => string '<b/>Bar</b>' (length=11) 
     3 => string ' 
</a>' (length=6) 
     4 => string ' 
Baz 
</a>' (length=10) 
    1 => 
    array (size=5) 
     0 => string '<a=5>' (length=5) 
     1 => string '<a=3>' (length=5) 
     2 => string '<b/>' (length=4) 
     3 => string '' (length=0) 
     4 => string '' (length=0) 
    2 => 
    array (size=5) 
     0 => string '' (length=0) 
     1 => string '' (length=0) 
     2 => string '/' (length=1) 
     3 => string '' (length=0) 
     4 => string '' (length=0) 
    3 => 
    array (size=5) 
     0 => string ' 
' (length=2) 
     1 => string ' 
Foo 
' (length=7) 
     2 => string 'Bar' (length=3) 
     3 => string ' 
' (length=2) 
     4 => string ' 
Baz 
' (length=6) 
    4 => 
    array (size=5) 
     0 => string '' (length=0) 
     1 => string '' (length=0) 
     2 => string '</b>' (length=4) 
     3 => string '</a>' (length=4) 
     4 => string '</a>' (length=4) 

Live Demo

OR

$re = "/(<[^\\/>]+\\/?>)*([^<]+)(<\\/\\w+>)*/m"; 
    $str = "<a=5>fff\n <a=3>\n Foo\n <b/>Bar</b>\n </a>\n Baz\n</a>"; 

    preg_match_all($re, $str, $matches); 
    //var_dump($matches); 
    $md=""; 
    $c=count($matches[1]); 
    foreach($matches[1] as $k=>$v){ 
     if($k!=0){ 
      $md.=$v.$matches[2][$k].$matches[3][$k]; 
     } 
     else if ($c!=$k+1){ 
      $md.=$matches[2][$k].$matches[3][$k]; 
     } 
    } 
var_dump($md); 

Live

區tput的

string 'fff 
<a=3> 
Foo 
<b/>Bar</b> 
</a> 
Baz 
</a>' (length=44) 
+0

我不明白你做了什麼。您的演示似乎會導致大量隨機匹配。 – Felk 2014-12-19 19:05:02