重访:Java正则表达式中的零长度匹配

时间:2013-07-30 16:00:23

标签: java regex

我从去年开始看这个问题:Zero-length matches in 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”或空字符串。当匹配器指向第一个“b”时,没有“a”,因此find()匹配空字符串。

然而,javadoc说:

  

此方法从此匹配器区域的开头开始,或者,如果是   以前的方法调用是成功的,匹配器有   从没有重置,在第一个字符不匹配   上一场比赛。

因此,当匹配器指向'b'时,没有匹配的字符,find()匹配空字符串,这意味着在此之后,第一个与前一个匹配不匹配的字符(即空字符串)仍将是'b'。根据上面的说法,这应该意味着下一个find()应该从同一个地方开始,这意味着代码应该无限循环。但当然,这不是正在发生的事情。当一个空字符串匹配时,它看起来只是将起点加1。

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

2 个答案:

答案 0 :(得分:1)

a?表示零或一个'a'字符,因此它会匹配a或“无” - 它匹配“a”字符和“虚无” “将定位在”a“和”b“字符之间。

这是完全正确和预期的。

答案 1 :(得分:1)

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

不同之处在于前一个匹配的边界存储在匹配器的状态中,而它们会影响下一个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”字符未与之前的任何一场比赛相匹配,也不应该被跳过。不过,这有点模糊不清。