为什么“。*”和“。+”给出不同的结果?

时间:2009-11-12 10:51:11

标签: regex

为什么“。*”和“。+”给出不同的结果?

System.out.println("foo".replaceAll(".+", "bar")); // --> "bar"
System.out.println("foo".replaceAll(".*", "bar")); //--> "barbar"

我希望两者都有“bar”,因为*和+都是贪婪的,应该匹配整个String。 (上面的例子是Java,但其他工具,如http://www.gskinner.com/RegExr/给我的结果相同)

7 个答案:

答案 0 :(得分:12)

你是贪婪的,但".*"匹配两个字符串是正确的:第一个是"foo",第二个是""".+"只会与"foo"匹配。

两者都尝试匹配最长的字符串"foo"。之后,他们尝试找到上一场比赛后最长的匹配字符串。在此阶段,".*"能够匹配空字符串,而".+"则不会。

答案 1 :(得分:9)

Mehrdad已经解释过它还匹配字符串末尾的一个空子字符串。我在.net文档中找到了这种行为的官方解释(为什么匹配一个空子字符串而不是无限数字):

http://msdn.microsoft.com/en-us/library/c878ftxe.aspx

  

量词*,+,{n,m}(以及它们的“懒惰”对应物)在匹配最小数量n的空匹配后永远不会重复。当m为无穷大时,此规则可防止量词在空匹配上进入无限循环(尽管规则适用,即使m不是无穷大)。

     

例如,(a?)*匹配字符串“aaa”并捕获模式(a)(a)(a)()中的子串。请注意,没有第五个空捕获,因为第四个空捕获导致量词停止重复。

答案 2 :(得分:2)

通过实验测试:在没有前进的情况下,replaceAll的匹配器在相同的字符串位置不会匹配两次。

实验:

System.out.println("foo".replaceAll(".??", "[bar]"));

输出:

[bar]f[bar]o[bar]o[bar]

说明:

模式.??是一个0或1个字符的非贪婪匹配,这意味着它将按优先级匹配任何内容,如果强制使用则匹配一个字符。在第一次迭代中,它不匹配任何内容,replaceAll在字符串的开头用""替换"[bar]"。在第二次迭代中,它将再次匹配,但是这是禁止的,因此将一个字符从输入复制到输出("f"),位置提前,再次尝试匹配等等所以你有bar - f - bar - o - bar - o - bar:每个可以匹配空字符串的不同位置有一个“[bar]”。最后没有可能推进,所以替换终止,但只有匹配“最终”空字符串之后。

出于好奇的考虑,Perl做了一些非常相似的事情,但是它以不同的方式应用规则,为同一输入和相同模式提供"[bar][bar][bar][bar][bar][bar][bar]"的输出 - .??仍然禁止制作在同一位置连续两次零宽度匹配,但允许回溯并匹配单个字符。意思是用“[bar]”替换“”,然后用“[bar]”替换“f”,然后用“[bar]”替换“”,然后用“[bar]”等替换“o”,直到最后字符串的零宽度匹配是禁止的,并且没有进一步的正宽度匹配。

答案 3 :(得分:1)

我的猜测是,贪婪的.*首先匹配整个字符串,然后开始从当前位置(字符串结尾)查找匹配项,并在退出前匹配空字符串。

答案 4 :(得分:0)

hm,两种情况下的Python都会生成'bar'

>>> import re
>>> re.sub('.+', 'bar', 'foo')
'bar'
>>> re.sub('.*', 'bar', 'foo')
'bar'

答案 5 :(得分:0)

这是一个非常有趣的问题。

当你考虑它时,String.replaceAll(...)可以在逻辑上实现,以执行“。*”案例中的三件事之一:

  • 做一次替换,给出“bar”
  • 做两个替换,给出“barbar”
  • 尝试无数次替换。

显然,最后一种选择没有用,所以我可以理解为什么他们不这样做。但我们不知道为什么他们选择“barbar”解释而不是“bar”解释。问题在于Regex语法没有通用标准,只有Regex语义。我的猜测是,太阳作者做了以下其中一项:

  • 查看其他预先存在的实现所执行和复制的内容,
  • 考虑并做了他们认为最好的,或
  • 没有考虑这种边缘情况,目前的行为是无意的。

但是在一天结束时,为什么他们选择“barbar”并不重要。事实是他们确实......我们只需要解决这个问题。

答案 6 :(得分:0)

我认为,第一轮两种模式(.+.*)都匹配所有字符串("foo")。之后,剩余的空字符串输入将与.*模式匹配。

但是,我从以下模式中发现了一个非常奇怪的结果。

^.*  => 'bar'
.*$  => 'barbar'
^.*$ => 'bar'

你能解释为什么它会返回上述结果吗?正则表达式中的起始字符串(^)和结束字符串($)之间有什么不同?

<强> Update.1

我尝试将输入字符串更改为以下字符串。

  

FOO

     

FOO

请看新结果!

'^。*'=&gt;

  

     

FOO

'。* $'=&gt;

  

foo

     

BARBAR

所以,我认为,每个输入只有一个开始字符串。另一方面,当函数在输入字符串中找到匹配字符串时,它不会删除当前当前字符串的结束字符串。 PS。您可以在http://gskinner.com/RegExr/

快速试用