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

Regex matching nested beginning and ending tags

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

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

编辑 3:我删除了支持标签的义务,但没有结束标签。我为未来的用户重新格式化了问题,为了理解下面的一些评论,请参阅 post

的第一个版本

更多,我让它同时适用于所有三个,给我三个匹配,这在网站 regex101 上不起作用。比赛中必须支持换行符。虽然,我可以接受只有最后两个组合给出两个匹配项,因为我可以将单独的 if 的标签更改为 iif.

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

您可以使用

~{{             # 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
     [^{]*(?:\{(?!{/?[^\{\}]*}})[^{]*)*
   )*           # Zero or more times
  )
 {{/}}        # Closing tag
~ix

regex demo

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

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

这是一个PHP demo:

$re = '~{{(\w+)\^([^^\{\}]?)\^([^\{\}]+)}}((?>(?R)|[^{]*(?:\{(?!{/?[^\{\}]*}})[^{]*)*)*){{/}}~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);