正则表达式匹配嵌套的开始和结束标记

时间:2016-05-11 13:53:03

标签: php regex pcre

以下是我想要在标记{{if}}{{\if}}之间提取包含的字符串,我的意思是第一个和最后一个(内部的将由引擎重新检查):

  • "在{{if ^^ p1 ^ p2}} IN1之前; {{if ^ ^ p1}} {{iif}} IN3 {{/ if}} IN1-1 {{/ if}}"
  • 之后
  • "在{{if ^ ^ p1}} IN1之前; {{if ^ ^ p1}} {{if ^ ^ p1}} IN3 {{/ if}} {{/ if}} IN1-1 {{/ if}}"
  • "在{{if ^ ^ p1}} IN1之前; {{if ^ ^ p1}} {{if ^ ^ p1}} IN3 {{/ if}} {{/ if}} IN1-1 {{if ^ ^ p1}} IN4 {{/ if}} {{/ if}}在"
  • 之后

正则表达式是:\{\{(if)\}\}(((?!\{\{\/?\1\}\})[\s\S])*(\{\{\1\}\}(?2)*\{\{\/\1\}\})*((?!\{\{\/?\1\}\})[\s\S])*)\{\{\/\1\}\}

编辑3:我删除了支持TAG而没有结束TAG的义务。我重新格式化了未来用户的问题,了解下面的一些评论,请参阅帖子的第一个版本

更多,我有三个同时适用于我的三个比赛,这在网站regex101上没有效果。在比赛中必须支持换行符。虽然,我可以接受只有最后两个组合会给出两个匹配,因为我可以为if更改单独iif的标记。

我的另一个解决方案是不使用正则表达式,但如果可能,我想这样做。

1 个答案:

答案 0 :(得分:3)

您可以使用

~{{             # Opening tag start
  (\w+)         # (Group 1) Tag name
  \^            # Aux delimiter
  ([^^\{\}]?)   # (Group 2) Specific delimiter
  \^            # Aux delimiter
  ([^\{\}]+)    # (Group 3) Parameters
 }}             # Opening tag end
  (             # (Group 4)
   (?>          
     (?R)       # Repeat the whole pattern
     |          # or match all that is not the opening/closing tag
     [^{]*(?:\{(?!{/?\1[^\{\}]*}})[^{]*)*
   )*           # Zero or more times
  )
 {{/\1}}        # Closing tag
~ix

请参阅regex demo

通常,表达式基于递归和tempered greedy token[^{]*(?:\{(?!{/?\1[^\{\}]*}})[^{]*)*部分是展开的(?s:(?!{{/?\1}}).)*模式,匹配任何不是.{{TAG}}字符序列起点的字符({{/TAG}})。< / p>

此模式不需要DOTALL修饰符,因为模式中没有.

这是PHP demo

$re = '~{{(\w+)\^([^^\{\}]?)\^([^\{\}]+)}}((?>(?R)|[^{]*(?:\{(?!{/?\1[^\{\}]*}})[^{]*)*)*){{/\1}}~i'; 
$str = "before {{if^^p1^p2}} IN1; {{if^ ^p1}} {{iif}} IN3 {{/if}} IN1-1 {{/if}} after\nbefore {{if^ ^p1}} IN1; {{if^ ^p1}} {{if^ ^p1}} IN3 {{/if}} {{/if}} IN1-1 {{/if}} after\nbefore {{if^ ^p1}} IN1; {{if^ ^p1}} {{if^ ^p1}} IN3 {{/if}} {{/if}} IN1-1 {{if^ ^p1}} IN4 {{/if}} {{/if}} after"; 
preg_match_all($re, $str, $matches);
print_r($matches);