我在一个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 ""
直到最后一场比赛很清楚,模式[哇] *贪婪地匹配0或更多' w'和' o'字符,而对于不匹配的字符,包括空格,它会导致空字符串。但是在匹配了最后一个' l'与11"",以下12""似乎不清楚。在测试解决方案中没有详细说明,我也无法从javadoc中明确地解决这个问题。我最好的猜测是边界特征,但如果有人能提供解释,我将不胜感激
答案 0 :(得分: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的时间,即最后一个字符的时间:这里," tail"由一个空字符串组成。这类似于调用"wow its cool".substring(12)
:在这种情况下你也会得到一个空字符串。
引擎将空字符串视为有效输入,并尝试将其与表达式匹配,如上例所示。这会产生匹配,程序会正确报告。
答案 1 :(得分:3)
[wow]*
匹配第一个wow
字符串。 count = 1
由于字符类旁边有*
(零或更多),[wow]*
这个正则表达式会匹配一个空字符串,该字符串存在于字符之前与上述模式不匹配。因此它匹配前面存在于第一个空间的边界或空白空间。数= 2。
its
与上述正则表达式不匹配。所以它匹配每个字符之前存在的空字符串。因此,计数为2+3=5
。
并且第二个空格与上述正则表达式不匹配。所以我们得到一个空字符串作为匹配。 5+1=6
c
与上述正则表达式不匹配。因此它匹配之前存在的空格c
6+1=7
oo
与上述正则表达式匹配。 [wow]*
。所以它匹配oo
,这被认为是1匹配。因此,我们将7+1=8
作为计数。
l
不匹配。 Count = 9
最后它匹配最后一个字符旁边的空字符串。所以现在计数是9+1=10
最后我们都知道m.start()
会打印相应匹配的起始索引。
答案 2 :(得分:0)
正则表达式只是将模式与输入匹配,从给定的偏移量开始。 对于最后一场比赛,12的偏移量是在' cool'的最后一个字符之后的点。 - 您可能认为这是字符串的结尾,因此不能用于匹配目的 - 但您错了。对于模式匹配,这是一个非常有效的起点。
正如您所说,您的正则表达式包含零字符的可能性,实际上,这是在最后一个字符结束之后但在字符串结束标记之前发生的事情(通常在正则表达式中由$表示)
换句话说,没有测试超过最后一个字符的结尾,这意味着不会发生与字符串结尾相关的匹配 - 但是有许多正则表达式结构与字符串的结尾相匹配(和你在这里展示了其中一个)。