正则表达式没有采取尽可能少的匹配

时间:2018-01-24 12:23:47

标签: php regex preg-match-all

我有这个正则表达式模式:

/(?J){% *(?P<tag>[a-zA-Z_]+) *(?P<args>[a-zA-Z0-9 _-]+) *%}(?P<block>.*){% *end(?P<tag>[a-zA-Z_]+) *%}/s

这个搜索字符串:

{% import add %}{% endimport %}
{% extends base.html %}{% endextends %}
{%       block              title %}
Changed
{% endblock %}
{% block content %}
Yay!
{% endblock %}

通过preg_match_all运行时,它会返回完整的搜索字符串,而不是第一个{% import add %}{% endimport %}。为什么,我该如何解决?

2 个答案:

答案 0 :(得分:0)

您有一个命名模式:(?P<block>.*)

将其更改为(?P<block>.*?)(在明星后添加?。)

一般注释:.*贪婪版本)等模式应与 非常小心,因为他们可能会消耗太多

您还可以对正则表达式进行进一步改进:

  • (?P<tag>[a-zA-Z_]+)的第二个实例更改为(?P=tag) - a 对第一次使用的tag组的反向引用。 我假设在end之后应该有相同的文字, 它捕获了第一个tag组。
  • 然后您可以删除(?J),因为没有多次出现任何命名模式 更多。
  • 也许您也应该将(?P<args>[a-zA-Z0-9 _-]+)更改为 (?P<args>[a-zA-Z0-9\. _-]+)(将字面点添加到允许的集合中 字符)。 或者将允许的字符列表更改为[^%]。 那么这个模式也会匹配 {% extends base.html %}{% endextends %}(样本的第一行)。

答案 1 :(得分:0)

正则表达式是&#34;贪心&#34;默认情况下 - 他们采用最长匹配,而不是最短

在这种情况下,您的问题似乎是.*令牌,它基本上转换为&#34;匹配任何内容&#34;。这将通过立即匹配字符串的整个剩余部分,然后反向跟踪来操作,直到可以满足正则表达式的后续部分。结果是,最后一个{% something %}标记的所有内容都被视为您的最终匹配。

对此最简单的解决方案是使用.*?,这意味着&#34;匹配任何东西,但不要贪图它#34;。这将从没有匹配开始,然后向前工作,直到模式可以匹配,可能会给你你想要的结果。

但是,正如评论中所述,标记化解析器可能更适合此类任务:跟踪字符串,将其分为标记序列,非标记,标记,非标记,然后匹配之后的标签。这将使您的语法更加灵活,并且可以减少嵌套标签等复杂性或检测格式错误的输入的麻烦。