为啥正则表达式默认是贪婪的?

Posted

技术标签:

【中文标题】为啥正则表达式默认是贪婪的?【英文标题】:Why are regular expressions greedy by default?为什么正则表达式默认是贪婪的? 【发布时间】:2011-01-17 12:20:57 【问题描述】:

对于编写正则表达式的初学者来说,这似乎是一个巨大的困惑源,可能会导致隐藏的性能问题,而且看起来典型的用例是非贪婪的。

这仅仅是出于遗留原因(它最初是如何完成的,并且每个实现都复制它),还是有它的原因?

【问题讨论】:

谁以主观和争论的态度投票结束,愿意详细说明? 正则表达式默认不是贪婪的,但是它们的量词是:-) 在我看来真正的问题是,为什么懒惰的量词比贪婪的量词支持更差和/或使用起来更尴尬? 这个问题也困扰着我,从逻辑上思考,创建一个惰性正则表达式引擎比贪婪引擎更有效和容易,他们应该将默认模式设置为惰性,因为这是人们认为的默认。另外,在我使用生命的所有正则表达式中,我不记得使用贪婪匹配的时间超过 1% 【参考方案1】:

一个典型的用例似乎是非贪婪的。

我想明确指出这是错误的,除非“典型用例”意味着 html 黑客攻击。

一个简单的例子是编程语言的词法分析器。你根本不想要

foo = 42

被解释为 3 个变量,后跟等号,后跟 2 个数字。相反,通常您希望解析器考虑最长的匹配项。

在 HTML 出现之前,我们这些年长的人已经用贪婪的正则表达式生活了几十年,而且我们做得很好。即使在今天,我在 99% 的情况下都不会使用非贪婪的,诚然是因为我懒得查找语法,但也因为在很少的情况下你不能只写一个终止良好的贪婪。例如,匹配一个字符串:

"(\\"|[^"])*"

【讨论】:

我认为词法分析器不会从寻找“寻找某物”中获得太多收益,同时也只是确保其他一切都“不是某物”。当然,对于由单个符号分隔的字符串,它看起来不错,但是您尝试对由多个分隔的其他内容执行此操作,它很快就会变得丑陋,例如多行注释:/\*((?!\*/).)*\*/。与/\*.*?\*/ 相比。添加的分隔符越多,情况就越糟,因为您必须否定所有分隔符。 Greedy 适用于大多数用例,因为这些问题不经常出现,但声称它有利于词法分析或在其他方面是错误的......【参考方案2】:

这里真正的问题是 Kleene 闭包运算符(星号);对于正则表达式中的其他所有内容,最长匹配与最短匹配相同。

当您从这些方面考虑时,您会意识到更现代的工具会意识到您需要两者。我迟到了,所以我只能想到两个例子:

kshbash 都提供大多数特殊变量更改运算符的“最长匹配”“最短匹配”形式。

Lua 正则表达式包括用于 Kleene 闭包最长匹配的 * 和用于 Kleene 闭包最短匹配的 -。当我忘记逃避文字 - 标志时,这个总是咬我。

回到 Kleene 的原始工作,看看这是否会影响早期工具的最长匹配,这会很有趣。

【讨论】:

交替呢?给定正则表达式/foo|foobar/ 和目标字符串blahfoobarblah,数学上纯正则表达式将始终匹配foobar,而Perl 派生的NFA 正则表达式将满足foo【参考方案3】:

歇斯底里的莱森斯


部分答案可能涉及实际计算中 RE 的起源。它们最初是 theoretical concept from automata theory and formal language theory,直到 Ken Thompson himself 编写了一个真正的实现并在 qed 和 ed(1) 中使用它们。

原始版本只有贪婪的语法,所以甚至没有做出决定。

【讨论】:

我不太确定你可以说理论上的正则语言默认是贪婪的。我认为 Kleene 正则表达式定义了一组可以匹配它的所有字符串,因此 /x*/ 可以匹配 "" 或 "x" 或 "xxx" (等等)。这样的表达式定义了包括字符串“”、“x”和“xxx”的常规语言。请注意,这并没有说明如何在正文中搜索匹配项。只有当你应用理论时,你才会开始关心贪婪。 当然,当然,我所说的“原始版本”我只是指“Ken Thompson 为那些编辑输入的”, 并且在那些版本中之后的近十年里,ed、grep、ex 和 vi 只进行了贪婪模式匹配。 啊,那样的话我们就同意了。【参考方案4】:

可能原因:The regex engine needs to backtrack a lot if it's non-greedy.

【讨论】:

@roe:是的,两种量词行为都需要回溯。【参考方案5】:

在性能方面,由于回溯,惰性量词并不总是更快:http://blog.stevenlevithan.com/archives/greedy-lazy-performance

至于实际设计,老实说,我不能说为什么量词默认是贪婪的,但我确实想知道使用什么控制字符来使量词变得贪婪而不是懒惰。我不认为? 会削减它:-)

【讨论】:

@forefinger:那不匹配字符串/行的结尾吗? 确实如此。这似乎是最好的贪婪符号。【参考方案6】:

嗯,计算机的行为尽可能可预测是很重要的。所以正确的行为应该遵循一个简单的规则,比如贪心匹配,这样至少有经验的程序员可以预测一段代码的结果。

至于一个典型的用例是否应该是非贪婪的,那么以下情况呢:假设我有一个包含 foo1909、bar3939、baz3331 等条目的文件,我只想提取这些数字。将 (\d*) 写为正则表达式似乎很自然。

您可能会说编写 (\d*)\D 或其他任何东西一样容易,但基本上总是这样,程序员可以更明确,更少含糊。因为我们想要一个 100% 可预测的默认行为,并且在头脑中计算起来很简单,所以这对我来说似乎是合理的。

【讨论】:

这是一个完全合乎逻辑和合理的猜测,然而,它与真正的原因完全无关,这只是非贪婪来得很久很久,所以它不是默认值。跨度>

以上是关于为啥正则表达式默认是贪婪的?的主要内容,如果未能解决你的问题,请参考以下文章

python-正则表达式

python正则表达式贪婪算法与非贪婪算法与正则表达式子模式的简单应用

正则贪婪,非贪婪,分组,前瞻

js正则匹配总结

正则表达式 U贪婪模式

正则表达式