关于贪婪与非贪婪
默认情况下,正则表达式中的重复是贪婪:他们尝试匹配尽可能多的代表,当这不起作用并且他们不得不回溯时,他们尝试在一个时间,直到找到整个模式的匹配。因此,当匹配最终发生时,贪婪的重复将尽可能匹配 许多 个重复。
? 作为重复量词将此行为更改为非贪婪,也称为不情愿 (in e.g. Java)(有时也称为“懒惰”)。相反,此重复将首先尝试匹配尽可能 少数 个重复,当这不起作用并且他们必须回溯时,他们开始一次匹配一个更多重复。因此,当匹配最终发生时,不情愿的重复将尽可能匹配 少数 次。
参考文献
示例 1:从 A 到 Z
让我们比较一下这两种模式:A.*Z 和 A.*?Z。
给定以下输入:
eeeAiiZuuuuAoooZeeee
这些模式产生以下匹配:
让我们首先关注A.*Z 的作用。当它匹配第一个A 时,.* 贪婪,首先尝试匹配尽可能多的.。
eeeAiiZuuuuAoooZeeee
\_______________/
A.* matched, Z can't match
由于Z 不匹配,引擎回溯,然后.* 必须匹配少一个.:
eeeAiiZuuuuAoooZeeee
\______________/
A.* matched, Z still can't match
这种情况又发生了几次,直到最后我们得出这样的结论:
eeeAiiZuuuuAoooZeeee
\__________/
A.* matched, Z can now match
现在Z可以匹配,所以整体模式匹配:
eeeAiiZuuuuAoooZeeee
\___________/
A.*Z matched
相比之下,A.*?Z 中的不情愿重复首先匹配尽可能少的.,然后根据需要匹配更多的.。这就解释了为什么它会在输入中找到两个匹配项。
以下是两种模式匹配内容的直观表示:
eeeAiiZuuuuAoooZeeee
\__/r \___/r r = reluctant
\____g____/ g = greedy
示例:另一种选择
在许多应用程序中,上述输入中的两个匹配项是所需的,因此使用不情愿的.*? 而不是贪婪的.* 以防止过度匹配。然而,对于这个特定的模式,有一个更好的选择,使用否定字符类。
模式A[^Z]*Z 还找到与上述输入(as seen on ideone.com)的A.*?Z 模式相同的两个匹配项。 [^Z] 是所谓的否定字符类:它匹配除Z 之外的任何字符。
两种模式的主要区别在于性能:更严格,否定字符类只能匹配给定输入的一种方式。对此模式使用贪婪或不情愿的修饰符并不重要。事实上,在某些方面,你可以做得更好,使用所谓的所有格量词,它根本不会回溯。
参考文献
示例 2:从 A 到 ZZ
这个例子应该是说明性的:它展示了贪婪、不情愿和否定字符类模式如何在相同的输入下以不同的方式匹配。
eeAiiZooAuuZZeeeZZfff
这些是上述输入的匹配项:
这是他们匹配的视觉表示:
___n
/ \ n = negated character class
eeAiiZooAuuZZeeeZZfff r = reluctant
\_________/r / g = greedy
\____________/g
相关主题
这些是指向 stackoverflow 上的问题和答案的链接,涵盖了一些可能感兴趣的主题。
一个贪婪的重复可以超过另一个