1

我从去年开始研究这个问题:Java Regex 中的零长度匹配

Pattern pattern = Pattern.compile("a?");
Matcher matcher = pattern.matcher("ababa");
while(matcher.find()){
   System.out.println(matcher.start()+"["+matcher.group()+"]"+matcher.end());
}

产生输出:

0[a]1
1[]1
2[a]3
3[]3
4[a]5
5[]5

我想知道这是否正确。该模式匹配“a”或空字符串。当 Matcher 指向第一个“b”时,没有“a”,所以 find() 匹配空字符串。

但是,javadoc说:

此方法从该匹配器区域的开头开始,或者,如果该方法的先前调用成功并且匹配器此后尚未重置,则从前一个匹配不匹配的第一个字符开始。

所以当Matcher指向'b'时,没有匹配到任何字符,而find()匹配的是一个空字符串,也就是说之后的第一个匹配不匹配的字符(即空字符串)仍然是'b '。这应该意味着下一个 find() 应该从同一个地方开始,根据上面的说法,这意味着代码应该无限循环。但当然这不是正在发生的事情。当匹配一个空字符串时,它看起来只是将起点提高了 1。

那么这是什么一回事?是实现错误,还是 javadoc 遗漏了什么,或者我遗漏了什么?

4

2 回答 2

1

好的,当 find() 返回一个空字符串时,看起来确实有一种特殊情况。为了清楚起见,由于我认为有些人不理解这个问题,我的问题是为什么匹配器状态在第二个 find() 和第三个 find() 之前应该不同,因为“第一个字符与前一个字符不匹配match" 在这两种情况下都是相同的。

不同之处在于上一个匹配的边界存储在匹配器的状态中,它们确实会影响下一个 find(),但仅在这种情况下。从 Matcher.java 中的 find() 代码:

 int nextSearchIndex = last;
 if (nextSearchIndex == first)
      nextSearchIndex++;

last是搜索开始的地方,除非最后一个 find() 返回一个空字符串(或firstlast其他方法设置),然后它向上移动一个。这个片段没有评论,所以我不确定目的是什么,但看起来它是故意制造一个空字符串匹配的特殊情况。但是,它似乎与 javadoc 相矛盾,因为在这种情况下,搜索从 javadoc 所说的位置以外的地方开始。

编辑:顺便说一句,这确实有一个看起来令人惊讶的后果:

 Pattern p = Pattern.compile("a?");
 Matcher m = p.matcher ("abcde");
 m.find();
 System.out.println("[" + m.group() + "]");
 m.find();
 System.out.println("[" + m.group() + "]");
 m.usePattern (Pattern.compile("[bd]"));
 m.find();
 System.out.println("[" + m.group() + "]");

输出

[a]
[]
[d]

最后一个匹配没有找到“b”,即使 'b' 字符没有被任何先前的匹配匹配并且不应该被跳过。不过,这有点晦涩难懂。

于 2013-07-30T18:07:40.440 回答
1

a?表示零个或一个 'a' 字符,因此它将匹配一个或一个“nothing” - 它匹配位于“ a”和“b”字符之间a的“ a”字符和“nothingness” 。

这是完全正确和预期的。

于 2013-07-30T16:03:45.457 回答