7

可能重复:
String.replaceAll() 异常与正则表达式中的贪婪量词

我正在编写使用Matcher#replaceAll并发现以下结果非常令人困惑的代码:

Pattern.compile("(.*)").matcher("sample").replaceAll("$1abc");

现在,我希望输出是sampleabc但是 Java 会向我抛出sampleabcabc

有人知道为什么吗?

现在,当然,当我锚定模式 ( ^(.*)$) 时,问题就消失了。我仍然不知道为什么地狱会replaceAll做这样的双重替换。

并且雪上加霜,下面的代码:

Pattern.compile("(.*)").matcher("sample").replaceFirst("$1abc")

按预期工作,仅返回sampleabc.

4

2 回答 2

5

出于某种原因,它看起来与输入末尾的空字符串匹配。(我知道它为什么会匹配;我很感兴趣它匹配一次且仅一次。)

如果换成replaceAll("$1abc")结果replaceAll("'$1'abc")'sample'abc''abc

请注意,如果您更改(.*)(.+)then 它可以正常工作,因为它必须匹配至少一个字符。

此代码确认诊断:

Matcher matcher = Pattern.compile("(.*)").matcher("sample");

while (matcher.find()) {
    System.out.printf("%d to %d\r\n", 
                      matcher.start(), 
                      matcher.end());
}

...输出:

0 to 6
6 to 6
于 2013-01-24T23:09:42.030 回答
5

这里有两件事可以解释为什么会发生这种情况:

  • (.*)将成功匹配空字符串。
  • 一场比赛成功后,将在上一场比赛结束后的一个位置尝试另一场比赛。

因此,在匹配整个字符串"sample"之后,会在 . 之后尝试另一个匹配e。即使没有剩余字符,匹配也会成功并发生第二次替换。

不会发生其他替换,因为正则表达式引擎将始终向前移动。就在最后一个字符是有效的起始索引之后,因此空字符串将匹配一次,但在匹配空字符串之后,正则表达式引擎不再有有效的起始位置来尝试匹配。

作为向正则表达式添加字符串锚点开头的替代方法,您可以修改正则表达式,使其匹配一个或多个字符,方法是更改(.*)​​为(.+).

于 2013-01-24T23:10:23.193 回答