为什么(.*)*会产生两个匹配结果,但在第一组中没有选择任何内容?

21

这起源于一个讨论正式化正则表达式语法的话题。我在几个正则表达式解析器中都看到了这种行为,因此我将其标记为与特定编程语言无关。

拿以下表达式为例(根据您喜欢的编程语言进行调整):

replace("input", "(.*)*", "$1")

它会返回一个空字符串。为什么?

更加奇怪的是,表达式replace("input", "(.*)*", "A$1B")将返回字符串ABAB。为什么有两个空匹配项?

免责声明:我知道回溯和贪婪匹配,但是Jeffrey Friedl所规定的规则似乎是.*匹配所有内容,不进行进一步的回溯或匹配。那么为什么$1是空的?

注意:(.+)*相比,后者会返回输入字符串。然而,http://regexhero.com显示仍然有两个匹配项,这看起来与上述原因相同,有点奇怪。


1
这个问题的答案可能取决于正则表达式的实现(例如,它在发现有效匹配后是否立即停止)。 - Richard
2
@Some1.Kill.The.DJ:确实几乎一样,但并非完全相同。这是针对特定编程语言的问题,答案并未解释这里所阐述的内容。最后,答案似乎包含了一个错误的说法(即(.*)*会将整个匹配结果放在$1中,而这正是这个问题所证明的不正确之处)。 - Abel
@Abel:在那个问题中,正则表达式是((.*)*),所以整个匹配结果将会被放置在$1中。(当然,外层括号完全是多余的,但答案是正确的。) - Tim Pietzcker
@Tim:你是对的,我只关注了这个句子:“_$1将包含与(.*)*/(.+)*匹配的内容,即整个字符串_”,并且解释错误了。 - Abel
1个回答

25

让我们看看会发生什么:

  1. (.*) 匹配 "input"
  2. "input" 被捕获到第一个组中 (1)。
  3. 正则表达式引擎现在位于字符串的末尾。但是由于 (.*) 重复了,因此会进行另一次匹配尝试:
  4. (.*) 匹配 "input" 后面的空字符串。
  5. 空字符串被捕获到第一个组中 (1),覆盖了 "input"
  6. $1 现在包含空字符串。

从评论中来的一个好问题:

那么为什么 replace("input", "(input)*", "A$1B") 返回 "AinputBAB"

  1. (input)* 匹配 "input"。它被替换为 "AinputB"
  2. (input)* 匹配空字符串。它被替换为 "AB"$1 因为没有参与匹配而为空)。
  3. 结果: "AinputBAB"

1
实际上,.* 匹配的是字符串开头和结尾的空字符串。我用 Matcher.find() 进行了尝试,并打印了 matcher.group(1)matcher.start()。奇怪的是,它打印出了位置 05,并匹配了空字符串。 - Rohit Jain
3
@samuil说:.+不能匹配空字符串。 - Tim Pietzcker
1
@RohitJain:我不使用Java,所以我无法重现这个问题。虽然听起来很奇怪,但我无法想象字符串开头会有一个空匹配。.*是贪婪的,而且没有回溯的理由... - Tim Pietzcker
1
只要不强制外部的*量词继续运行,它就会停止。 - Tim Pietzcker
1
@AndriyF。我认为对于replace("input", "(.*)", "A$1B")也是如此,它返回AinputBAB。同样,replace("input", "(.*)*", "A$1B")由于Tim解释的原因返回ABAB。请注意,他更新了他的答案以回应您的评论。 - Abel
显示剩余13条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接