为啥正则表达式默认是贪婪的?
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 闭包运算符(星号);对于正则表达式中的其他所有内容,最长匹配与最短匹配相同。
当您从这些方面考虑时,您会意识到更现代的工具会意识到您需要两者。我迟到了,所以我只能想到两个例子:
ksh
和 bash
都提供大多数特殊变量更改运算符的“最长匹配”和“最短匹配”形式。
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% 可预测的默认行为,并且在头脑中计算起来很简单,所以这对我来说似乎是合理的。
【讨论】:
这是一个完全合乎逻辑和合理的猜测,然而,它与真正的原因完全无关,这只是非贪婪来得很久很久,所以它不是默认值。跨度>以上是关于为啥正则表达式默认是贪婪的?的主要内容,如果未能解决你的问题,请参考以下文章