【问题标题】:How to iterate over regex expression如何迭代正则表达式
【发布时间】:2013-05-24 21:34:26
【问题描述】:

假设我有以下字符串:

name1=gil;name2=orit;

我想找到name=value 的所有匹配项,并确保整个字符串与模式匹配。

所以我做了以下事情:

  1. 确保整个模式与我想要的匹配。

    Pattern p = Pattern.compile("^((\\w+)=(\\w+);)*$");
    Matcher m = p.matcher(line);
    if (!m.matches()) {
        return false;
    }
    
  2. 遍历模式name=value

    Pattern p = Pattern.compile("(\\w+)=(\\w+);");
    Matcher m = p.matcher(line);
    while (m.find()) {
        map.put(m.group(1), m.group(2));
    }
    

有没有办法用一个正则表达式来做到这一点?

【问题讨论】:

    标签: java regex


    【解决方案1】:

    您可以通过以下方式使用一个正则表达式验证和迭代匹配项:

    • 通过在我们的正则表达式开头放置一个\G 来确保匹配之间没有不匹配的字符(例如name1=x;;name2=y;),这意味着"the end of the previous match"

    • 通过将字符串的长度与Matcher.end() 进行比较来检查我们是否在最后一次匹配时到达了字符串的末尾,Matcher.end() 返回匹配最后一个字符之后的偏移量。

    类似:

    String line = "name1=gil;name2=orit;";
    Pattern p = Pattern.compile("\\G(\\w+)=(\\w+);");
    Matcher m = p.matcher(line);
    int lastMatchPos = 0;
    while (m.find()) {
       System.out.println(m.group(1));
       System.out.println(m.group(2));
       lastMatchPos = m.end();
    }
    if (lastMatchPos != line.length())
       System.out.println("Invalid string!");
    

    Live demo.

    【讨论】:

    • 如果我想允许分隔符(例如,','),但不允许在输入的开头或结尾处使用,我该如何调整此 Pattern?
    【解决方案2】:

    您必须为“^”和“$”启用多行模式才能按预期工作。

    Pattern p = Pattern.compile("^(?:(\\w+)=(\\w+);)*$", Pattern.MULTILINE);
    while (m.find()) {
        for (int i = 0; i < m.groupCount() - 2; i += 2) {
            map.put(m.group(i + 1), m.group(i + 2));
        }
    }
    

    正确的注释,您仍然必须遍历每行的匹配组,并使外部组成为非捕获组(?:...)

    【讨论】:

    • 默认情况下正则表达式引擎在多行模式下匹配。你想使用 dotall 选项吗!。同样给出有问题的例子,你的正则表达式不会工作..
    • @Anirudh:不,默认情况下,Java 中未启用 MULTILINE 模式。 DOTALL 选项在这里将无用。
    【解决方案3】:
    String example = "name1=gil;name2=orit;";
    Pattern pattern = Pattern.compile("((name[0-9]+?=(.+?);))+?");
    Matcher matcher = pattern.matcher(example);
    // verifies full match
    if (matcher.matches()) {
        System.out.println("Whole String matched: " + matcher.group());
        // resets matcher
        matcher.reset();
        // iterates over found
        while (matcher.find()) {
            System.out.println("\tFound: " + matcher.group(2));
            System.out.println("\t--> name is: " + matcher.group(3));
        }
    }
    

    输出:

    Whole String matched: name1=gil;name2=orit;
        Found: name1=gil;
        --> name is: gil
        Found: name2=orit;
        --> name is: orit
    

    【讨论】:

    • 这确实是 1 个正则表达式,但它需要对输入字符串进行 2 次传递(一次在 matches() 中,一次在 find() 循环中)
    • @nhahtdh 你是对的。但我不知道这里有任何“1 pass”限制。
    • 并不是真正的限制,只是说它与OP当前的解决方案在遍数方面没有太大区别。
    • @nhahtdh 你说得有道理。导致我这样做的是,我猜 OP 的解决方案没有显示嵌套通道。
    猜你喜欢
    • 2015-04-18
    • 1970-01-01
    • 1970-01-01
    • 2022-06-23
    • 1970-01-01
    • 1970-01-01
    • 2015-09-24
    • 1970-01-01
    • 2015-06-25
    相关资源
    最近更新 更多