正则表达式匹配块内的块

时间:2014-08-20 15:21:42

标签: php regex

我有以下字符串:

{% if a == 3 %}
    Yes
{% else %}
    {% if b == 2 %}
        Maybe
    {% else %}
        {% if c == 1 %}
            Hm... Not
        {% else %}
            No way!
        {% endif %}
    {% endif %}
{% endif %}
{% if d == 0 %}
    Ok
{% endif %}

如果我使用这个正则表达式 /\{%\s*if\s*(.*?)\s*%\}(.*)(\{%\s*else\s*%\}(.*))?\{%\s*endif\s*%\}/ism (在PHP上的preg_match_all函数中),我的返回是上面的所有代码。但是当我使用时 /\{%\s*if\s*(.*?)\s*%\}(.*?)(\{%\s*else\s*%\}(.*?))?\{%\s*endif\s*%\}/ism (刚出贪婪模式),我的回复结束于{% endif %} {% if c == 1 %} if(找到第一个endif)。

我希望获得以下回报:

1

{% if a == 3 %}
    Yes
{% else %}
    {% if b == 2 %}
        Maybe
    {% else %}
        {% if c == 1 %}
            Hm... Not
        {% else %}
            No way!
        {% endif %}
    {% endif %}
{% endif %}

2

{% if d == 0 %}
    Ok
{% endif %}

我的正则表达式如何归档此回报?

PS。我知道,如果我在ifs上添加一些名称并在endif上使用此名称,则可以使用反向引用轻松解决...但我不想要一个姑息性的答案

提前致谢。

2 个答案:

答案 0 :(得分:3)

您可以使用此递归模式:

$pattern = '~{% if [^%]+%}(?>[^{]+|(?R))*(?>{% else %}(?>[^{]+|(?R))*)?{% endif %}~';

online demo

模式细节:

~
{% if [^%]+%}
(?>                  # this atomic group describes the content
                     # between if/else/endif markups:
    [^{]+            #  - all that is not an opening curly bracket
  |                  # OR
    (?R)             #  - recursion (repeat the whole pattern)
)*                   # repeat the group zero or more times
(?>
    {% else %}       # The "else" part works exactly in the same way,
    (?>[^{]+|(?R))*
)?                   # but is optional
{% endif %}
~

答案 1 :(得分:0)

我扩展了这个例子来解决我自己的问题,特别是在其中有一些随机的{%...%}:regex101 online demo