Regex:当一个特定组中有多个时,无法检测到所有出现的情况

  • 本文关键字:情况 一个 Regex php regex regex-group
  • 更新时间 :
  • 英文 :


我正试图运行一个正则表达式(在PHP中(来检测和提取条件及其匹配项,但我被困在elseif场景中(可能会重复出现(。

这是我当前的正则表达式:

/{%if (.+)%}(.*)(?:{%elseif (.+)%}(.*)(?={%))*?(?:{%else%}(.*))*{%endif%}/gsU

这是我想通过的测试,目前在最后一组条件下失败(您也可以在regex101上看到(:

{%if $foo === "bar" && getThisCondition()%}
My if result
{%endif%}
{%if $foo === "bar" && getThisCondition()%}
My if result
{%else%}
My else result
{%endif%}
{%if $foo === "bar" && getThisCondition()%}
My if result
{%elseif $foo === "bar" && getThisCondition()%}
My elseif result
{%endif%}
{%if $foo === "bar" && getThisCondition()%}
My if result
{%elseif $foo === "baz" && !getThisCondition()%}
My elseif result
{%else%}
My else result
{%endif%}
{%if $foo === "bar" && getThisCondition()%}
My if result
{%elseif $foo === "baz" && !getThisCondition()%}
My elseif result
{%elseif $foo === "baf" && !getThisCondition()%}
My elseif result
{%elseif $foo === "bak" && !getThisCondition()%}
My elseif result
{%else%}
My else result
{%endif%}

如何确保所有elseif事件都被考虑在内?当我隔离它们(并移除*?(时,它起作用:

(?:{%elseif (.+)%}(.*)(?={%))*?/gsU

但如果我把它放回整个表达式中,它就不起作用了。

我错过了什么?

正如注释中所说,您无法在PHP中的重复捕获组中检索所有捕获,因为每次重复捕获组时都会覆盖捕获内容。

如果不可能在一场比赛中抓住所有你想要的,那并不意味着你不能在一种模式中做到。您可以使用preg_match_all(或preg_replace_callback(来检索条件语句的每一部分,这种模式首先检查完整的条件语句是否格式良好,然后逐个获取不同的部分:

~
(?(DEFINE)
(?<full> {%if     g<cond> %}  g<cont>
(?: {%elseif g<cond> %}  g<cont> )*
(?: {%else%}               g<cont> )?
{%endif%}
)
(?<cond> [^%]*+ (?: % (?!}) [^%]* )*+ )
(?<cont> [^{]*+ (?: { (?!%) [^{]* )*+ )
)
(?J) # allow duplicate named captures
(?=g<full>) # check if a well formed if/elseif/else/endif is at this position
{%if (?<condition> g<cond> ) %} (?<content> g<cont> )
|
G (?<= {%endif%} ) (*SKIP)(*F) # break the contiguity after {%endif%}
|
G {%elseif (?<condition> g<cond> ) %} (?<content> g<cont> )
|
G {%else%} (?<content> g<cont> )
|
G {%endif%}
~xu

演示

在与以(?=g<full>)开始的分支成功匹配之后,在与锚G成功匹配之后从连续位置匹配语句的所有其他部分(注意,第二分支G (?<= {%endif%} ) (*SKIP)(*F)在这里是为了在到达语句末尾时打破这种连续性(。

使用这种模式,您所要做的就是在匹配结果上循环,以检查新的if语句何时开始。

请注意,您可以使用非捕获组G(?: branch2 | branch3 ...)G放在模式的所有最后分支的因子中,更好的是,您可以将其替换为G(?!A)序列,以避免在字符串开头出现不合适的匹配(因为默认情况下G在字符串开头成功(。

要知道哪个分支成功了,可以创建一个捕获组,比如stmt,以捕获语句:%(?<stmt>if)%(?<stmt>elseif),等等。但你也可以使用一个更有趣的替代方法,使用可以标记的(*MARK)控制动词(你所要做的就是把它放在相应的分支(*MARK:if)(*MARK:elseif)…中的某个地方(。使用preg_match_all,会创建一个MARK项,并在结果数组中填充标签,但它不适用于preg_replace_callback

相关内容

最新更新