正则表达式:为什么^。*(\ ba \ w * \ b)?。* $没有捕获任何东西?

时间:2012-11-07 17:15:17

标签: java regex

以下Java代码旨在捕获单词“abc”,但它提供“null”:

Pattern p = Pattern.compile("^.*(\\ba\\w*\\b)?.*$");
Matcher m = p.matcher("xxx abc yyy");
if (m.matches()) System.out.println(m.group(1));

如果删除问号,则会正确捕获“abc”。问号是贪心的,所以我原本认为原始代码也应该给出“abc”。

感谢任何能解释原因的人!

2 个答案:

答案 0 :(得分:5)

正则表达式开头的.*是贪婪的,所以它最初会尝试匹配尽可能多的字符(整个字符串)。当正则表达式引擎移动到捕获组时,它会发现\ba\w*\b在字符串末尾无法匹配,但由于该组是可选的,因此它不会回溯并尝试查找匹配项。

要解决此问题,只需将开头的.*更改为.*?,它仍会匹配零个或多个字符,但会尝试尽可能少地匹配(懒惰而不是贪婪):

Pattern p = Pattern.compile("^.*?(\\ba\\w*\\b)?.*$");

另一种选择是通过删除后面的?来使您的捕获组成为必需。这会强制正则表达式引擎回溯,直到进行组匹配。这可能不是你想要的,因为它会改变正则表达式的含义(匹配的字符串会更少)。

编辑:看起来我真的应该测试一下这个!事实证明只是将.*更改为.*?在这里没有用,因为您的群组在开头仍然无法匹配,整个字符串将与.*匹配最后(即使你把它改为.*?)。

这里最好的选择是删除组后的?,以便需要该组。如果您仍想匹配所有字符串,但对于与您的组不匹配的字符串,该组为null,则可以使用以下正则表达式:

^(?:.*(\ba\w*\b).*|.*)$

答案 1 :(得分:1)

F.J。关于原因是正确的。


要在行上显式匹配以a开头的第一个word-char序列,您可以匹配任何数量的非单词字符或以a以外的ASCII字母开头的单词,然后是一个可选的捕获的a字,可能后跟被忽略的东西。

此程序按预期打印abc

import java.util.regex.*;

public class Foo {
  public static void main(String[] argv) {
    Pattern p = Pattern.compile("^(?:\\W|[b-zA-Z]\\w+)*(?:(a\\w*)?(?:.*))$");
    Matcher m = p.matcher("xxx abc yyy");
    if (m.matches()) System.out.println(m.group(1));
  }
}

正则表达式是明确的,因此只需要在字符串上进行一次正向传递。但确实需要仔细阅读。

我倾向于这些情况通常是明确地标记 - 分成单词和非单词然后循环遍历数组寻找你想要的东西。


或者,您可以使用find代替match使用非锚定的正则表达式。

  

find()尝试查找与模式匹配的输入序列的下一个子序列。

所以你可以做到

Pattern p = Pattern.compile("(\\ba\\w*\\b)?");
Matcher m = p.matcher("xxx abc yyy")
while (m.find()) { System.out.println(m.group(1)); }

或如果您只想要第一个while,请将if替换为$


最后,$并不意味着java中的输入结束。它表示输入结束或输入结束时的换行符之前。 javadoc解释了端锚之间的细微差别:

  

\Z行的结尾
  \z输入的结束,但是对于最终的终结符,如果有的话   {{1}}输入的结尾

相关问题