【问题标题】:Java Regex Matcher skipping the matchesJava Regex Matcher 跳过匹配项
【发布时间】:2018-05-10 14:43:05
【问题描述】:

以下是我的 Java 代码,用于删除所有匹配的相邻字母对,但 Java Matcher 类出现了一些问题。

我的方法

我正在尝试在输入中查找所有连续重复的字符,例如

aaa, bb, ccc, ddd

接下来将奇数长度匹配替换为最后匹配的模式,将偶数长度匹配替换为"",即

aaa -> a
bb -> ""
ccc -> c
ddd -> d
s has single occurrence, so it's not matched by the regex pattern and excluded from the substitution

我正在调用 Matcher.appendReplacement 以根据组长度(偶数或奇数)对输入中匹配的模式进行条件替换。

代码:

public static void main(String[] args) {
        String s = "aaabbcccddds";
        int i=0;
        StringBuffer output = new StringBuffer();
        Pattern repeatedChars = Pattern.compile("([a-z])\\1+");
        Matcher m = repeatedChars.matcher(s);
        while(m.find()) {
            if(m.group(i).length()%2==0)
                m.appendReplacement(output, "");
            else
                m.appendReplacement(output, "$1");
            i++;
        }
        m.appendTail(output);
        System.out.println(output);
    }

输入:aaabbcccddds

实际输出:aaabbcccds(仅将ddd替换为d,但跳过aaabbccc

预期输出:acds

【问题讨论】:

  • Expected Output : acds 应该是 acs 因为 d 在偶数位置。
  • 它不是在位置上,而是在匹配模式的长度上 - 偶数或奇数。 'd'-s 连续重复的子串长度为 3,为奇数,应替换为组 'd'
  • 代码跳过了前三个模式,只替换了最后一个匹配项。这就是我面临的问题。因为我只调用了一次 m.find(),然后我调用了 m.group(i),这也不是我的期望

标签: java regex string pattern-matching matcher


【解决方案1】:

这可以在单个 replaceAll 调用中完成,如下所示:

String repl = str.replaceAll( "(?:(.)\\1)+", "" );

正则表达式(?:(.)\\1)+ 匹配所有出现的偶数重复并将其替换为空字符串,这样我们就得到了奇数重复的第一个字符。

RegEx Demo


使用PatternMatcher 的代码:

final Pattern p = Pattern.compile( "(?:(.)\\1)+" );
Matcher m = p.matcher( "aaabbcccddds" );
String repl = m.replaceAll( "" );
//=> acds

【讨论】:

  • 这个也很好用。已投赞成票。希望使用 Java 模式匹配器。谢谢你的回答。
  • 这个代码很容易翻译成使用Pattern + Matcher。我会更新,它仍然是很短的代码。
  • 这是否包括偶数和奇数的重复?
  • 是的,当然,您可以查看链接的演示或在不同的输出上运行此代码。
  • 一开始没注意。完全没有逻辑。当我比较我们的两种解决方案时,我感觉很长。 @somnathchakrabarti 我鼓励你接受这个作为唯一正确的解决方案。
【解决方案2】:

你可以这样尝试:

public static void main(String[] args) {
    String s = "aaabbcccddds";
    StringBuffer output = new StringBuffer();
    Pattern repeatedChars = Pattern.compile("(\\w)(\\1+)");
    Matcher m = repeatedChars.matcher(s);
    while(m.find()) {
        if(m.group(2).length()%2!=0)
            m.appendReplacement(output, "");
        else
            m.appendReplacement(output, "$1");
    }
    m.appendTail(output);
    System.out.println(output);
}

它与您的相似,但是当仅获取第一组时,您匹配第一个字符并且您的长度始终为 0。这就是为什么我引入第二组,它是匹配的相邻字符。由于它的长度为 -1,我反转了奇偶逻辑,瞧 -

广告

被打印出来。

【讨论】:

  • 是的,s可以在中间或任何地方
  • 好的,明白了!我的错误是我假设该组将返回所有匹配的模式,但它仅适用于正则表达式中的括号绑定部分 - 只有 1 或 2 而不是 m.group(i)。非常感谢
  • 大量代码可以在单个 replaceAll 方法调用中完成:)
  • 但我试图使用 Pattern + Matcher 来查看如何将单行正则表达式替换转换为 Java 代码。问题的复杂性不是问题。 :)
【解决方案3】:

您不需要多个 if 语句。试试:

(?:(\\w)(?:\\1\\1)+|(\\w)\\2+)(?!\\1|\\2)

替换为$1

Regex live demo

Java 代码:

str.replaceAll("(?:(\\w)(?:\\1\\1)+|(\\w)\\2+)(?!\\1|\\2)", "$1");

Java live demo

正则表达式分解:

  • (?:非捕获组开始
    • (\\w)捕获一个单词字符
    • (?:\\1\\1)+匹配偶数个相同字符
    • |或者
    • (\\w)捕获一个单词字符
    • \\2+匹配任意数量的相同字符
  • )非捕获组结束
  • (?!\\1|\\2) 后面没有之前捕获的字符

PatternMatcherStringBuffer 一起使用:

StringBuffer output = new StringBuffer();
Pattern repeatedChars = Pattern.compile("(?:(\\w)(?:\\1\\1)+|(\\w)\\2+)(?!\\1|\\2)");
Matcher m = repeatedChars.matcher(s);
while(m.find()) m.appendReplacement(output, "$1");
m.appendTail(output);
System.out.println(output);

【讨论】:

  • 这个也可以,而且在正则表达式方面它是一个更好的解决方案。我也会接受这个作为答案,但因为我正在寻找使用 Java Pattern Matcher 的东西。总之,点赞!非常感谢您快速正确的回复
  • 不客气。我提供了一个基于此的解决方案。
猜你喜欢
  • 1970-01-01
  • 2021-04-16
  • 2013-08-21
  • 2015-06-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多