Java正则表达式边界匹配?

4

我在一个Java测试套件中找到了以下问题

    Pattern p = Pattern.compile("[wow]*");
    Matcher m = p.matcher("wow its cool");
    boolean b = false;
    while (b = m.find()) {
        System.out.print(m.start() + " \"" + m.group() + "\" ");
    }

输出似乎如下所示

0 "wow" 3 "" 4 "" 5 "" 6 "" 7 "" 8 "" 9 "oo" 11 "" 12 ""

在最后一次匹配之前很清楚,模式[wow]*会贪婪地匹配0个或多个'w'和'o'字符,而对于不匹配的字符(包括空格),则结果为空字符串。然而,在将最后一个'l'与11""匹配后,接下来的12""似乎不太清楚。测试解决方案中没有详细说明,而我也无法从javadoc中明确找出答案。我最好的猜测是边界字符,但如果有人能提供解释,我会非常感激。


注意:这里有一个更简单的例子:http://ideone.com/z0B7H2。 - Oliver Charlesworth
我猜当达到s.length()时(其中s为“wow its cool”),即12时,它就停止了。 - Charlie
请查看此链接 https://regex101.com/r/eQ2aB0/4 。如果有任何问题,请提出。 - Avinash Raj
你是指 (wow)* 这个正则表达式,它表示 "wow" 可以出现任意次数,还是指可以无序、任意次数地出现 wo。也就是说,第二个 w 是多余的。 - Peter Lawrey
从问题中可以清楚地看出,OP知道发生了什么(即他不会对“cool”的“oo”被匹配感到惊讶)。 - Sergey Kalinichenko
如果有人来这里寻求解决方案,这可能是离题的:while (b=m.find() && m.start()<s.length()) - Charlie
3个回答

3
你看到这种行为的原因是你的模式允许空匹配。换句话说,如果你给它一个空字符串,你会在位置零看到一个单独的匹配:
Pattern p = Pattern.compile("[wow]*"); // One of the two 'w's is redundant, but the engine is OK with it
Matcher m = p.matcher("");             // Passing an empty string results in a valid match that is empty
boolean b = false;
while (b = m.find()) {
    System.out.print(m.start() + " \"" + m.group() + "\" ");
}

这将打印 0 "",因为空字符串与表达式的任何其他匹配一样好。

回到您的示例,每次引擎发现匹配项(包括空匹配项)时,它都会向前移动一个字符。 "向前移动一个" 意味着引擎在下一个位置考虑字符串的“尾巴”。这包括正则表达式引擎位于第11个位置,即最后一个字符时:此时,“尾部”由一个空字符串组成。这类似于调用 "wow its cool".substring(12):在这种情况下,您也会得到一个空字符串。

引擎将空字符串视为有效输入,并尝试将其与您的表达式匹配,如上例所示。这将产生一个匹配项,您的程序正确报告了它。


这个解释似乎讲得很清楚。非常感谢您提供详细的答案。 - hammerfest

3
  • [wow]* 匹配第一个 wow 字符串。计数 = 1

  • 由于字符类旁边有 *零个或多个),所以 [wow]* 正则表达式将匹配一个空字符串,该空字符串存在于上述模式未匹配的字符之前的空间。因此,它匹配了在第一个空格之前存在的边界或空格。计数 = 2。

  • its 与上述正则表达式不匹配。因此,它匹配每个字符之前存在的空字符串。因此计数为 2+3=5

  • 并且第二个空格也未被匹配。因此我们得到一个空字符串作为匹配项。5+1=6

  • c 与上述正则表达式不匹配。因此,它匹配刚好在 c 之前存在的空字符串。 6+1=7

  • oo 与上述正则表达式匹配。 [wow]*。因此,它匹配了 oo 并被视为 1 个匹配项。因此我们得到的计数是 7+1=8

  • l 与上述正则表达式不匹配。计数 = 9

  • 最后,它会匹配紧接在最后一个字符旁边的空字符串。因此现在的计数为 9+1=10

  • 最后我们都知道 m.start() 打印相应匹配的起始索引。

演示


哦,我明白了。那么在这里谈论“计数”的目的是什么?这不是OP所指的度量标准。 - Oliver Charlesworth
@OliverCharlesworth 是的,那部分已经被das覆盖了。 - Avinash Raj

0
正则表达式只是针对输入的模式进行匹配,从给定偏移量开始。 对于上一次匹配,偏移量12位于“cool”的最后一个字符之后 - 您可能认为这是字符串的末尾,因此不能用于匹配目的 - 但您将错。 对于模式匹配,这是一个完全有效的起点。
正如您所述,您的正则表达式包括零个字符的可能性,实际上,在最后一个字符之后但在字符串结束标记(通常在正则表达式中表示为$)之前会出现这种情况。
换句话说,如果不测试超过最后一个字符,则意味着永远不会发生与字符串结尾有关的任何匹配 - 但是有许多正则表达式构造可与字符串结尾匹配(您在此处展示了其中之一)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接