【问题标题】:Regular Expression For Duplicate Words重复单词的正则表达式
【发布时间】:2010-05-12 21:51:39
【问题描述】:

我是一个正则表达式新手,我不知道如何编写一个正则表达式来“匹配”任何重复的连续单词,例如:

巴黎在春天。

不是那个是相关的。

你笑什么? my my 正则表达式有那么糟糕吗?

是否有一个正则表达式可以匹配上面所有的粗体字符串?

【问题讨论】:

  • @poly:这不是“指责”,而是一个冷静、正常的问题,完全可以用“不”作为答案。 @Joshua:是的,有些人(不是太少)让这个网站为他们做功课。但是,当它们被标记为这样时,在 SO 上提出家庭作业问题并不是一件坏事。通常答案的风格会从“这里是解决方案”变为“这里有一些你没有想到的事情”,这是一件好事。必须有人努力保持这种区别,在他的情况下是我,而在其他地方“其他人”也做同样的事情。就是这样。
  • 希望永远不要看到“这听起来有点像职场问题。是吗?”这样的问题。然后人们会争论堆栈溢出是否正在做某人的工作。
  • @Joshua +1 关于您接受的正则表达式解决方案,您能否告诉我如何用一对元素中的一个元素替换匹配项(重复项)(例如,not that that is related -> @ 987654322@)?提前致谢
  • @Joshua 我想我找到了解决方案:我应该替换为 \1!
  • @DavidLeal \b(\w+)\s+(\1\s*)+\b 怎么样?

标签: regex duplicates capture-group


【解决方案1】:

试试这个正则表达式:

\b(\w+)\s+\1\b

这里\b 是一个单词边界,\1 引用了捕获的第一组匹配项。

Regex101 示例here

【讨论】:

  • 让我好奇; \0 也可以吗? (其中\0 是整个正则表达式,直到当前点或\0 是指整个正则表达式)
  • @Pindatjuh:不,我不这么认为,因为那个子比赛也是整个比赛的一部分。
  • 至少适用于 Eclipse 搜索/替换对话框中使用的正则表达式引擎。
  • 只是一个警告,这不处理带有撇号或(如 Noel 提到的)连字符的单词。 Mike 的解决方案在这些情况下效果更好
  • 此外,它不会捕获三次(或更多),而不是当 dup/triplicate 之一位于字符串末尾时
【解决方案2】:

我相信这个正则表达式可以处理更多的情况:

/(\b\S+\b)\s+\b\1\b/

可以在此处找到精选的测试字符串:http://callumacrae.github.com/regex-tuesday/challenge1.html

【讨论】:

  • 很好,可与撇号/连字符/等一起使用。太 - 谢谢!
  • 对于challenge1链接,你在替换区放置什么来使用分组词?试过 <strong>\0</strong> 但没用。
  • 它不会捕获三次(或更多),而不是当 dup/triplicate 之一位于字符串末尾时
  • @uptownhr 你想使用$1 <strong>$2</strong>。但也可以使用不同的正则表达式/\b(\S+) (\1)\b/gi。这是一个链接:callumacrae.github.io/regex-tuesday/…
  • 如果我想从特定标签中查找所有连续的单词,例如<p class="bebe">bla bla</p>,我该如何整合这个正则表达式公式?
【解决方案3】:

下面的表达式应该可以正确地找到任意数量的连续单词。匹配可以不区分大小写。

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

Matcher m = p.matcher(input);

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

示例输入:再见,再见 GooDbYe

示例输出:再见

解释:

正则表达式:

\b : 单词边界的开始

\w+ : 任意数量的单词字符

(\s+\1\b)* :任意数量的空格后跟单词,匹配前一个单词并结束单词边界。用 * 包裹的整个内容有助于找到不止一个重复。

分组:

m.group(0) : 应包含上述情况下的匹配组 Goodbye goodbye GooDbYe

m.group(1) : 应包含上述情况下匹配模式的第一个单词 Goodbye

替换方法将所有连续匹配的单词替换为单词的第一个实例。

【讨论】:

    【解决方案4】:

    用下面的 RE 试试这个

    • \b 单词边界开始
    • \W+ 任意单词字符
    • \1 个相同的单词已经匹配
    • \b 词尾
    • ()* 再次重复

      public static void main(String[] args) {
      
          String regex = "\\b(\\w+)(\\b\\W+\\b\\1\\b)*";//  "/* Write a RegEx matching repeated words here. */";
          Pattern p = Pattern.compile(regex, Pattern.CASE_INSENSITIVE/* Insert the correct Pattern flag here.*/);
      
          Scanner in = new Scanner(System.in);
      
          int numSentences = Integer.parseInt(in.nextLine());
      
          while (numSentences-- > 0) {
              String input = in.nextLine();
      
              Matcher m = p.matcher(input);
      
              // Check for subsequences of input that match the compiled pattern
              while (m.find()) {
                  input = input.replaceAll(m.group(0),m.group(1));
              }
      
              // Prints the modified sentence.
              System.out.println(input);
          }
      
          in.close();
      }
      

    【讨论】:

      【解决方案5】:

      正则表达式去除2+重复词(连续/非连续词)

      试试这个正则表达式,它可以捕获 2 个或更多重复的单词,并且只留下一个单词。而且重复的单词甚至不需要连续

      /\b(\w+)\b(?=.*?\b\1\b)/ig
      

      这里,\b 用于字边界,?= 用于正向前瞻,\1 用于反向引用。

      Example Source

      【讨论】:

      • 不连续是个坏主意:"the cat sat on the mat" -> " cat sat on the mat"
      • @Walf 真。然而,在某些情况下这是有意的。 (例如:同时抓取数据)
      • 为什么在I corrected it 之后又是break your regex again?你以为我改变了它的意图吗?即使您链接的示例也没有错误。
      • 是的,这是一个错误,复制粘贴了错误的东西。实际上打算从我的示例中复制一个。无论如何,它现在可以工作了!一切都很好!谢谢!
      • 我有一个类似的用例从 java 中的字符串中删除重复字符,您的解决方案帮助了我。谢谢。如果其他人正在寻找从 java 中的 String 中删除重复字符的代码 - s1.replaceAll("(.)(?=.*?\\1)", "")
      【解决方案6】:

      广泛使用的 PCRE 库可以处理这种情况(但是,您不会使用兼容 POSIX 的正则表达式引擎实现 the):

      (\b\w+\b)\W+\1
      

      【讨论】:

      • 您需要匹配两个词之间的字符,例如\W+\b 不会这样做,因为它不消耗任何字符。
      • 这可能会在... the these problems... 等情况下导致误报匹配。这种解决方案不如 Gumbo 模式的一般结构可靠,后者充分实现了单词边界。
      • 如果我想从特定标签中找到所有连续的单词,例如<p class="bebe">bla bla</p>,我该如何整合这个正则表达式?
      【解决方案7】:

      没有。那是不规则的语法。您可能可以使用特定于引擎/语言的正则表达式,但没有通用的正则表达式可以做到这一点。

      【讨论】:

      • 虽然从严格意义上来说是正确的,但我相信没有不支持分组和反向引用的正则表达式引擎不再被认真使用。
      【解决方案8】:

      这是我用来删除 twitch 机器人中重复短语的正则表达式:

      (\S+\s*)\1{2,}
      

      (\S+\s*) 查找任何不是空格的字符串,后跟空格。

      \1{2,} 然后在字符串中查找超过 2 个该短语的实例以进行匹配。如果有 3 个短语相同,则匹配。

      【讨论】:

      • 这个答案具有误导性。它不会搜索重复项,它会搜索出现 3 次或更多次的子字符串。由于捕获组中的\s*,它也不是很健壮。看这个演示:regex101.com/r/JtCdd6/1
      • 更极端的情况(低频文本)会产生误报匹配。例如。 I said "oioioi" that's some wicked mistressship! oioioisss
      【解决方案9】:

      这是一个多次捕获多个单词的方法:

      (\b\w+\b)(\s+\1)+
      

      【讨论】:

      • 如果我想从特定标签中查找所有连续的单词,例如<p class="bebe">bla bla</p>,我该如何整合这个正则表达式?
      • 我相信这需要 HTML 解析。对于您希望搜索的任何给定标签,找到 HTML 中出现的所有标签,然后逐个运行此正则表达式。或者,如果您不关心重复发生在 HTML 中的哪个位置,请连接所有标记文本属性并在连接的字符串上运行正则表达式
      • 我自己找到了答案<p class="bebe">.*?\b\s+(\w+)\b\K\s+\1\s+\b(?=.*?<\/p>)
      【解决方案10】:

      Javascript 中的示例:The Good Parts 可以进行调整:

      var doubled_words = /([A-Za-z\u00C0-\u1FFF\u2800-\uFFFD]+)\s+\1(?:\s|$)/gi;
      

      \b 使用 \w 作为单词边界,其中 \w 等价于 [0-9A-Z_a-z]。如果您不介意这种限制,那么接受的答案就可以了。

      【讨论】:

        【解决方案11】:

        由于一些开发人员来到此页面寻找一种解决方案,该解决方案不仅可以消除重复的连续非空白子字符串,而且可以消除三次及以上,因此我将展示调整后的模式。

        模式:/(\b\S+)(?:\s+\1\b)+/ (Pattern Demo)
        替换:$1(将全串匹配替换为捕获组#1)

        此模式贪婪地匹配“整个”非空白子字符串,然后需要一个或多个匹配子字符串的副本,这些副本可能由一个或多个空白字符(空格、制表符、换行符等)分隔。

        具体来说:

        • \b(单词边界)字符对于确保不匹配部分单词至关重要。
        • 第二个括号是非捕获组,因为不需要捕获此可变宽度子字符串 - 只需匹配/吸收即可。
        • 非捕获组上的+(一个或多个量词)比* 更合适,因为* 会“打扰”正则表达式引擎来捕获和替换单例事件——这是一种浪费的模式设计.

        *请注意,如果您正在处理带有标点符号的句子或输入字符串,那么模式将需要进一步细化。

        【讨论】:

        • @AdamJones 在您的 php 项目中使用此模式。 Nico 的回答中有一些不必要的语法。
        【解决方案12】:

        这个表达式(灵感来自上面的 Mike)似乎可以捕捉到所有重复、三次等,包括字符串末尾的那些,而大多数其他表达式都没有:

        /(^|\s+)(\S+)(($|\s+)\2)+/g, "$1$2")
        

        我知道要求匹配 duplicates 的问题,但一式三份只是彼此相邻的 2 个重复项:)

        首先,我输入(^|\s+) 以确保它以完整的单词开头,否则“child's steak”会转到“child'steak”(“s”会匹配)。然后,它匹配所有完整的单词 ((\b\S+\b)),然后是字符串结尾 ($) 或多个空格 (\s+),整个重复不止一次。

        我试过这样,效果很好:

        var s = "here here here     here is ahi-ahi ahi-ahi ahi-ahi joe's joe's joe's joe's joe's the result result     result";
        print( s.replace( /(\b\S+\b)(($|\s+)\1)+/g, "$1"))         
        --> here is ahi-ahi joe's the result
        

        【讨论】:

        • 我在将其重写为 PHP 时遇到了麻烦,我得到一个匹配副本的单个副本替换每次出现的重复/三次重复等非常重要。到目前为止,我有:preg_replace('/(^| \s+)(\S+)(($|\s+)\2)+/im', '$0', $string);
        • 这是最好的答案。我只是通过在末尾添加\b 对此进行了调整,如下所示:/(^|\s+)(\S+)(($|\s+)\2)+\b/g, "$1$2") 这将适用于以下情况:the the string String string stringing the the along the the string 将变为the string stringing the along the string 注意string stringing。它与您的答案相匹配。谢谢。
        【解决方案13】:

        试试这个适用于所有重复单词情况的正则表达式:

        \b(\w+)\s+\1(?:\s+\1)*\b
        

        【讨论】:

          【解决方案14】:

          如果您希望对重复单词进行不区分大小写的检查,请使用此选项。

          (?i)\\b(\\w+)\\s+\\1\\b
          

          【讨论】:

          • 使用不区分大小写的模式修饰符对你的模式没有用处。标志没有影响的字母范围。
          • 这实际上是已接受答案的副本,不会为页面增加任何价值。请考虑删除此答案以减少页面膨胀。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-11-19
          • 2016-03-17
          • 2019-09-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-07-01
          相关资源
          最近更新 更多