2014-12-19 63 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