为什么“。*”和“。+”给出不同的结果?
System.out.println("foo".replaceAll(".+", "bar")); // --> "bar"
System.out.println("foo".replaceAll(".*", "bar")); //--> "barbar"
我希望两者都有“bar”,因为*和+都是贪婪的,应该匹配整个String。 (上面的例子是Java,但其他工具,如http://www.gskinner.com/RegExr/给我的结果相同)
答案 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(...)
可以在逻辑上实现,以执行“。*”案例中的三件事之一:
显然,最后一种选择没有用,所以我可以理解为什么他们不这样做。但我们不知道为什么他们选择“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/
快速试用