正则表达式查找块之间的模式

Regex find pattern between blocks

我正在 PHP 中构建一个模板引擎(类似 Django),它用相关数据替换 {{ }} 之间的所有内容。现在我能够做到这一点,但我面临的情况是只需要在块之间进行替换,例如 {% for y in x %} 循环块并忽略所有不在它们之间的括号。

我在 this regex101 example 中得到了一些结果,但只得到了每个块的第一个 {{ }}。我想要做的是匹配每个块中的所有 {{ }},不包括外面的那些。

出于学习目的(非常好!)您有几种可能性:

  1. 多步骤方法(更容易理解和维护):

  2. 一个完整的正则表达式解决方案(更复杂,可能更"fancy")


广告 1)

用以下表达式匹配块(参见a demo on regex101.com):

{%\ for.*?%}
(?s:.+?)
{%\ endfor.*?%}

然后在每个块中寻找成对的{{...}}

{{\s*(.+?)\s*}}

PHP 中,这可能是:

<?php
$data = <<<DATA
{% for user in users %}
   Hello, {{ user.name }}, you are {{ user.age }} {{ user.name }}
ssssssssssssssssssssss {{ user.name }}
sdsddddddddddddddddddddddddddddd
{% endfor %}

{% for dog in dogs %}
   Your dog is {{ dog.age }} and likes {{ dog.food }}.
{% endfor %}
wwww
{{ user.name }}
DATA;

$block = '~
            {%\ for.*?%}
            (?s:.+?)
            {%\ endfor.*?%}
            ~x';

$variable = '~{{\s*(.+?)\s*}}~';

if (preg_match_all($block, $data, $matches)) {
    foreach ($matches as $match) {
        if (preg_match_all($variable, $match[0], $variables, PREG_SET_ORDER)) {
            print_r($variables);
        }

    }
}
?>


广告 2)

将所有有问题的变量与一个整体表达式匹配。在这里,您需要 \G(匹配最后一场比赛的位置)和一些先行(参见 a demo for this one at regex101.com as well):

(?:{%\ for.+?%}
|
\G(?!\A)
)
(?s:(?!{%).)*?\K
{{\s*(?P<variable>.+?)\s*}}

现在让我们揭开这个表达式的神秘面纱:

(?:{%\ for.+?%}
|
\G(?!\A)
)

在这里,我们想要在最后一个位置匹配 {%\ for.+?%}(我们需要 \,因为我们处于详细模式)\G 匹配。现在,事实是,\G 要么在最后一个匹配的位置匹配,要么在字符串的最开头匹配。我们不想要后者,因此否定。前瞻 (?!\A)

下一部分

(?s:(?!{%).)*?\K

有点像 "fast forward" 对有问题的有趣部分进行了处理。

分解,这个说

(?s:        # open a non-capturing group, enabling the DOTALL mode
    (?!{%). # neg. lookahead, do not overrun {% (the closing tag)
)*?         # lazy quantifier for the non-capturing group
\K          # make the engine "forget" everything to the left

现在,剩下的就简单了:

{{\s*(?P<variable>.+?)\s*}}

它基本上与广告 1) 的结构相同。

同样,在 PHP 中,这可能是:

<?php

$regex = '~
            (?:{%\ for.+?%}
            |
            \G(?!\A)
            )
            (?s:(?!{%).)*?\K
            {{\s*(?P<variable>.+?)\s*}}
            ~x';

if (preg_match_all($regex, $data, $variables)) {
    print_r($variables[1]);
}
?>


综上所述,实际学习更复杂的模式通常是个好主意,但另一方面不要重新发明轮子——总有比你我更聪明的人可能已经考虑了一些边缘情况,等等。