【问题标题】:Issue with Java regex for finding repeating words "\\b(\\w+)(\\s+\\1\\b)+"用于查找重复单词“\\b(\\w+)(\\s+\\1\\b)+”的 Java 正则表达式问题
【发布时间】:2018-08-07 23:29:10
【问题描述】:

我最近正在解决一个问题,以删除字符串中的重复单词,即“我很好”变成“我很好”。但我注意到一些奇怪的事情,正则表达式适用于所有不同的情况,除了一种情况,我不明白为什么。

这是我的代码:

        String regex = "\\b(\\w+)(\\s+\\1\\b)+";
        Pattern p = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);

        String input = "INPUT";

        Matcher m = p.matcher(input);

        // Check for subsequences of input that match the compiled pattern
        while (m.find()) {
            input = input.replaceAll(m.group(), m.group(1));
        }

        // Prints the modified sentence.
        System.out.println(input);

现在一旦给出INPUT:

我是凌晨 2 点,凌晨 1 点是个好人

输出:

我凌晨 2 点凌晨 1 点是个好人

仍然有两个重复的“am”。 现在如果 INPUT 是:

我是凌晨 2 点,凌晨 1 点,我是个好人

输出:

我凌晨 2 点凌晨 1 点是个好人

没有重复的“am”

我不知道为什么会这样,有人可以帮忙吗?

【问题讨论】:

  • 您的 Matcher 正在查找过时的输入,因为您在查找循环的中间修改了输入。所以“am am am”会被发现,但不再存在可替换。
  • @PatrickParker 我不确定你的意思,这如何证明 INPUT 选项 2 的输出是合理的,我希望 OPTION 2 返回 i am 2 am am 1 am a good man

标签: java regex string replace pattern-matching


【解决方案1】:

你想多了。

所有代码都可以替换为:

System.out.println(input.replaceAll("(?i)\\b(\\w+)(\\s+\\1\\b)+", "$1"));

用捕获组 1 替换匹配的文本。


无论如何,这是最优解。由于您似乎想要解释为什么您的代码失败了,这里是:

如果您对代码进行了调试,那么它失败的原因就很明显了。

在代码中添加 3 个打印语句会显示问题:

while (m.find()) {
    System.out.printf("group() = \"%s\", group(1) = \"%s\"%n", m.group(), m.group(1));
    System.out.printf("  input (before) = \"%s\"%n", input);
    input = input.replaceAll(m.group(), m.group(1));
    System.out.printf("  input (after) = \"%s\"%n", input);
}

输出

group() = "am am", group(1) = "am"
  input (before) = "i am am 2 am am am 1 am a good man"
  input (after) = "i am 2 am am 1 am a good man"
group() = "am am am", group(1) = "am"
  input (before) = "i am 2 am am 1 am a good man"
  input (after) = "i am 2 am am 1 am a good man"

如您所见,问题在于第二个匹配项仍然与 原始 输入匹配,匹配 am am am,但对 replaceAll() 的第一次调用已删除那些 am的。

修复代码的一种方法是调用replaceFirst() 而不是replaceAll(),使其尽可能接近您所拥有的。您还应该引用这些值,因为这两种方法都将正则表达式作为参数。

while (m.find()) {
    input = input.replaceFirst(Pattern.quote(m.group()), Matcher.quoteReplacement(m.group(1)));
}

【讨论】:

  • 我不是在寻找解决方案,我在对上一个回复的评论中有解决方案说明。我想知道是什么让这两个输入不同
  • 哦,所以你决定编辑你的评论哈哈。好的,我会调查你的解释。谢谢
  • 好的,我添加了调试日志并在我的帖子group() = "am am", group(1) = "am" input (before) = "i am am 2 am am am 1 am am a good man" input (after) = "i am 2 am am 1 am a good man" group() = "am am am", group(1) = "am" input (before) = "i am 2 am am 1 am a good man" input (after) = "i am 2 am am 1 am a good man" group() = "am am", group(1) = "am" input (before) = "i am 2 am am 1 am a good man" input (after) = "i am 2 am 1 am a good man" i am 2 am 1 am a good man中打印了第二个输入选项的输出@
  • 有没有理由为什么那个人没有在第二个循环处停下来?正如您在示例中展示的那样?这里有什么我想念的吗!
  • 我确实调试了代码,但您的解释并没有回答问题,事实上,在我调试代码这么久之后,我意识到正在发生这种情况,我不确定您是否正确理解我的问题,如果您复制代码并添加打印语句并为我给出的两个字符串运行它,您可以自己查看,不要这么快投票,我不是要求某人调试。
【解决方案2】:

不是这样的。

一方面,当您执行Matcher m = p.matcher(input); 时,Matcher 将应用于input 对象这是一个不可变字符串

你可能会认为,当你重新分配它时,你正在改变它

input = input.replaceAll(m.group(), m.group(1));

但是,不,您只是让input 变量引用一个新字符串。但是匹配器仍然使用旧字符串。

要对此进行测试,请添加调试行,并替换为更改后的字符串:

   while (m.find()) {
      System.out.println("input=[" + input +"] group=[" + m.group()  +"] group(1)=["+m.group(1)+"]");
       input = input.replaceAll(m.group(), m.group(1) + "x");
   }

这会产生:

input=[i am am 2 am am am 1 am am a good man] group=[am am] group(1)=[am]
input=[i amx 2 amx am 1 amx a good man] group=[am am am] group(1)=[am]
input=[i amx 2 amx am 1 amx a good man] group=[am am] group(1)=[am]
i amx 2 amx am 1 amx a good man

看看,尽管有 ìnputvariable having (after the first loop) noam am` 子字符串,匹配器仍能找到它们。

本着您的方法的精神(有更优雅或更高效的方法),可能是一种修复

   while( true ) {
      Matcher m = p.matcher(input);
      if(!m.find()) break;
      input = input.replaceAll(m.group(), m.group(1) );
   }

或者简单一点:

   while( true ) {
      String modif = input.replaceAll("\\b(\\w+)(\\s+\\1\\b)", "$1");
      if(modif.equals(input)) break;
      input = modif;
   }

【讨论】:

  • 如果我将当前的 while 循环替换为:while (m.find()) { input = input.replaceAll(m.group(), m.group(1)); m = p.match(input); } 将解决它,但我不是在寻找更类似于为什么会发生这种情况的解决方案,这两个输入之间有什么区别?
  • i am am 2 am am am 1 am am a good man 产生:input=[i am am 2 am am am 1 am am a good man] group=[am am] group(1)=[am] input=[i am 2 am am 1 am a good man] group=[am am am] group(1)=[am] input=[i am 2 am am 1 am a good man] group=[am am] group(1)=[am] i am 2 am 1 am a good man 和另一个选项产生input=[i am am 2 am am am 1 am a good man] group=[am am] group(1)=[am] input=[i am 2 am am 1 am a good man] group=[am am am] group(1)=[am] i am 2 am am 1 am a good man 我不明白还是很抱歉!我希望第二个 OPTION 输出i am 2 am am 1 am a good man
猜你喜欢
  • 2017-12-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-18
  • 1970-01-01
  • 2018-01-21
  • 2022-01-12
  • 2011-05-17
相关资源
最近更新 更多