【问题标题】:Regex matching with Java Matcher won't find as expected无法按预期找到与 Java Matcher 匹配的正则表达式
【发布时间】:2015-01-15 20:18:12
【问题描述】:

这里是the regex 我遇到了问题:^(?:(\S+?)(?:\s+|\s*$))。 我正在尝试在以下String 中匹配此模式的 3 次出现: -execution thisIsTest1 thisIsTest2。这是获取第一个 numberOfArgs 元素并返回填充匹配项的 List<String> 的方法。问题是:返回的List的大小是1....循环总是迭代一次然后退出...

private final String arguments="-execution  thisIsTest1  thisIsTest2";
 /**
 * Split the first N arguments separated with one or more whitespaces.
 * @return the array of size numberOfArgs containing the matched elements.
 */
...
public List<String> fragmentFirstN(int numberOfArgs){
    Pattern patt = Pattern.compile("^(?:(\\S+?)(?:\\s+|\\s*$))",Pattern.MULTILINE);
    Matcher matc = patt.matcher(arguments);
    ArrayList<String> args = new ArrayList<>();
    logg.info(arguments);
    int i = 0;
    while(matc.find()&&i<numberOfArgs){
        args.add(matc.group(1));
        i++;
    }
    return args;
}

这里是测试类:

private String[] argArr={"-execution",
        "thisIsTest1",
        "thisIsTest2"
};
...
@Test
public void testFragmentFirstN() throws Exception {
    List<String> arr = test.fragmentFirstN(3);
    assertNotNull(arr);
    System.out.println(arr); ----> prints : [-execution]
    System.out.println(test.getArguments()); ----> prints : -execution  thisIsTest1  thisIsTest2 <-----
    assertEquals(argArr[0],arr.get(0));
--->assertEquals(argArr[1],arr.get(1));<---- IndexOutOfBoundException : Index: 1, Size: 1
    assertEquals(argArr[2],arr.get(2));
    assertEquals(3,arr.size());
}

我认为Matcher#find() 在循环时会匹配所有可能的字符序列。我错过了什么?

【问题讨论】:

  • 我认为答案是^匹配输入字符串的开头,这意味着find只会找到第一个--跳过它之后,你不再在输入字符串的开头。我相信有一个正则表达式命令或Matcher 方法来移动锚点,使其与前一个find 停止的点相匹配,但我需要检查文档,因为我不记得了我的头顶。
  • 尝试使用\G 代替^(即Java 字符串文字中的\\G)。
  • @ajb :在这里!我怎么会错过它...非常感谢您的帮助。

标签: java regex matcher


【解决方案1】:

问题是正则表达式有一个边界匹配器,它匹配输入字符串的 start^ 字符)。第一次在循环中调用Matcher.find(),匹配的子字符串是-execution。这是因为-execution 从字符串的开头开始,并且正则表达式具有(?:\\s+|\\s*$) 部分,这意味着在输入字符串的末尾检测空格字符(-execution 之后的情况)或非空格字符.

第二次迭代将不匹配任何字符串,因为匹配器不再位于输入字符串的开头。因此Matcher.find() 返回 false。

您可以尝试删除字符:

Pattern patt = Pattern.compile("(?:(\\S+?)(?:\\s+|\\s*$))",
            Pattern.MULTILINE);

编辑:

基于@ajb 的cmets,只需删除^ 字符即可使正则表达式匹配以空格开头的输入字符串。如果不需要,您可以将^ 替换为\G,这标志着匹配器上一次匹配的结束:

Pattern patt = Pattern.compile("\\G(?:(\\S+?)(?:\\s+|\\s*$))",
            Pattern.MULTILINE);

【讨论】:

  • 但是,如果原始字符串以空格开头,我认为这会改变行为?如果程序单独检查以空格开头的字符串,它可能会起作用。
  • @ajb 如果字符串以空格开头,匹配器会在第一次迭代中跳过这个空格,不是吗?
  • 实际上不是以空格开头的,但对我的程序来说并不重要。不过谢谢!
  • 我认为原始正则表达式会拒绝该字符串,因为正则表达式实际上以 ^\S 开头。删除 ^ 会改变这种行为。
  • @ajb 我明白你的意思。如果不接受输入开头的空格,我更新了答案。
猜你喜欢
  • 2016-11-23
  • 1970-01-01
  • 1970-01-01
  • 2017-09-10
  • 2015-10-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多