正则表达式没有进行最少的匹配
Regex not taking the least possible match
我有这个正则表达式模式:
/(?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 %}
。为什么,我该如何解决?
您有一个命名模式:(?P<block>.*)
。
改为(?P<block>.*?)
(在星号后加?
)。
一般说明:像.*
这样的模式(greedy版本)应该与
极度小心,因为他们可能会消耗 far 太多。
您还可以进一步改进您的正则表达式:
- 将
(?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 %}
(样本的第一行)。
正则表达式默认为 "greedy" - 它们采用 最长 可能的匹配,而不是 最短.
在这种情况下,您的问题似乎是 .*
标记,它基本上转换为 "match anything at all"。这将通过立即匹配字符串的整个剩余部分来操作,然后 回溯 直到可以满足正则表达式的后续部分。结果是最后一个 {% something %}
标签之前的所有内容都被视为您的最终匹配项。
最简单的解决方案是只使用 .*?
,这意味着 "match anything, but don't be greedy about it"。这将从不匹配任何内容开始,然后继续前进,直到可以匹配模式,可能会为您提供所需的结果。
但是,如评论中所述,标记化解析器可能更适合此类任务:跟踪整个字符串,将其分成一系列标签、非标签、标签、非标签,然后之后匹配标签。这将使您的语法更加灵活,减少对嵌套标签等复杂性或检测格式不正确的输入的困扰。
我有这个正则表达式模式:
/(?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 %}
。为什么,我该如何解决?
您有一个命名模式:(?P<block>.*)
。
改为(?P<block>.*?)
(在星号后加?
)。
一般说明:像.*
这样的模式(greedy版本)应该与
极度小心,因为他们可能会消耗 far 太多。
您还可以进一步改进您的正则表达式:
- 将
(?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 %}
(样本的第一行)。
正则表达式默认为 "greedy" - 它们采用 最长 可能的匹配,而不是 最短.
在这种情况下,您的问题似乎是 .*
标记,它基本上转换为 "match anything at all"。这将通过立即匹配字符串的整个剩余部分来操作,然后 回溯 直到可以满足正则表达式的后续部分。结果是最后一个 {% something %}
标签之前的所有内容都被视为您的最终匹配项。
最简单的解决方案是只使用 .*?
,这意味着 "match anything, but don't be greedy about it"。这将从不匹配任何内容开始,然后继续前进,直到可以匹配模式,可能会为您提供所需的结果。
但是,如评论中所述,标记化解析器可能更适合此类任务:跟踪整个字符串,将其分成一系列标签、非标签、标签、非标签,然后之后匹配标签。这将使您的语法更加灵活,减少对嵌套标签等复杂性或检测格式不正确的输入的困扰。