Regex匹配嵌套的开始和结束标记


Regex matching nested beginning and ending tags

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

  • "在{{if^^p1^p2}}IN1之前;{if^ ^p1}}
  • "在{{if^^p1}}IN1之前;{if^ ^p1}
  • "在{{if^^p1}}IN1之前;{if^ ^p1}

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

第3版:我取消了支持TAG的义务,但没有终止TAG。我为未来的用户重新格式化了这个问题,为了理解下面的一些评论,请参阅文章的第一个版本

更重要的是,我让它同时适用于所有三个,给我三个匹配,这在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
     [^{]*(?:'{(?!{/?'1[^'{'}]*}})[^{]*)*
   )*           # Zero or more times
  )
 {{/'1}}        # Closing tag
~ix

查看regex演示

一般来说,该表达式是基于递归和经过调和的贪婪令牌的。[^{]*(?:'{(?!{/?'1[^'{'}]*}})[^{]*)*部分是展开的(?s:(?!{{/?'1}}).)*模式,其匹配不是{{TAG}}{{/TAG}}字符序列的起点的任何字符(.)。

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

下面是一个PHP演示:

$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);