【问题标题】:Regex matching a word containing a character exactly two times in a row正则表达式匹配包含一个字符的单词在一行中恰好出现两次
【发布时间】:2026-01-03 00:35:01
【问题描述】:

问题

如标题所述,我的目标是找到一个匹配一个单词的正则表达式,当且仅当它包含 exactly 两个连续字符的子字符串,并且没有被同一个字符包围。

测试用例

  • Helo --> false
  • programming --> true
  • belllike --> false(因为有三个ls)
  • shellless --> true(即使有三个ls,由于两个ss,这个输入应该匹配

我之前尝试过的事情

正则表达式[a-zA-Z]*([a-zA-Z])\1[a-zA-Z]* 匹配具有至少两个连续字符的单词,但belllike 仍然匹配,因为连续字符没有上限。

我还尝试使用负前瞻和后瞻。对于一封信,可能如下所示:

[a-zA-Z]*(?<!a)aa(?!a)[a-zA-Z]*

此正则表达式满足字母 a 的所有要求,但我和我询问的人都不能将其概括为使用捕获组,因此适用于任何字母(复制粘贴此语句 26 次 - 每个字母一次 - 和将它们与 OR 结合不是我正在寻找的解决方案,即使它可能会起作用)。

我在寻找什么

当然,所描述的问题的解决方案会很棒。如果不能用正则表达式完成,我同样很乐意解释为什么这是不可能的。

背景

这项任务是我必须为 uni 完成的一项任务的一部分。在对话中,教授后来表示他们实际上并不想问这个问题,并且可以接受三个或更多相同字符的字符序列。然而,试图为这个问题找到解决方案的努力激发了我对正则表达式是否真的可以做到这一点以及如何做到这一点的兴趣。

使用正则表达式风格

即使最初的任务应该在 Java 8+ 正则表达式风格中完成,我也可以使用任何正则表达式风格的解决方案来解决所描述的问题。

【问题讨论】:

    标签: regex regex-lookarounds regex-group


    【解决方案1】:

    你可以试试:

    ^(?:.*?(.)(?!\1))?(.)\2(?!\2).*$
    

    查看demo

    • ^ - 起始线锚点。
    • (?: - 打开非捕获组:
      • .*? - 除了换行符(惰性)之外的 0+ 个字符;
      • (.)(?!\1) - 除换行符之外的单个字符的第一个捕获组,但使用持有对该字符的反向引用的负前瞻断言它后面没有相同的字符。
      • )? - 关闭非捕获组并将其设为可选。
    • (.)\2(?!\2) - 与之前相同的构造,不同之处在于这次在第二个捕获组之间存在反向引用,并且断言位置的负前瞻后跟完全相同的字符。
    • .* - 除了换行符(贪婪)之外的 0+ 个字符;
    • $ - 结束线锚。

    对此的可视化:

    【讨论】:

      【解决方案2】:

      使用

      ^(.)\1(?!\1)|(.?)(?!\2)(.)\3(?!\3)
      

      proof

      解释

      --------------------------------------------------------------------------------
        ^                        the beginning of the string
      --------------------------------------------------------------------------------
        (                        group and capture to \1:
      --------------------------------------------------------------------------------
          .                        any character except \n
      --------------------------------------------------------------------------------
        )                        end of \1
      --------------------------------------------------------------------------------
        \1                       what was matched by capture \1
      --------------------------------------------------------------------------------
        (?!                      look ahead to see if there is not:
      --------------------------------------------------------------------------------
          \1                       what was matched by capture \1
      --------------------------------------------------------------------------------
        )                        end of look-ahead
      --------------------------------------------------------------------------------
       |                        OR
      --------------------------------------------------------------------------------
        (                        group and capture to \2:
      --------------------------------------------------------------------------------
          .?                       any character except \n (optional
                                   (matching the most amount possible))
      --------------------------------------------------------------------------------
        )                        end of \2
      --------------------------------------------------------------------------------
        (?!                      look ahead to see if there is not:
      --------------------------------------------------------------------------------
          \2                       what was matched by capture \2
      --------------------------------------------------------------------------------
        )                        end of look-ahead
      --------------------------------------------------------------------------------
        (                        group and capture to \3:
      --------------------------------------------------------------------------------
          .                        any character except \n
      --------------------------------------------------------------------------------
        )                        end of \3
      --------------------------------------------------------------------------------
        \3                       what was matched by capture \3
      --------------------------------------------------------------------------------
        (?!                      look ahead to see if there is not:
      --------------------------------------------------------------------------------
          \3                       what was matched by capture \3
      --------------------------------------------------------------------------------
        )                        end of look-ahead
      

      如果正则表达式支持无限宽度的lookbehinds:

      (.)\1(?!\1)(?<!\1..)
      

      proof

      解释

      --------------------------------------------------------------------------------
        (                        group and capture to \1:
      --------------------------------------------------------------------------------
          .                        any character except \n
      --------------------------------------------------------------------------------
        )                        end of \1
      --------------------------------------------------------------------------------
        \1                       what was matched by capture \1
      --------------------------------------------------------------------------------
        (?!                      look ahead to see if there is not:
      --------------------------------------------------------------------------------
          \1                       what was matched by capture \1
      --------------------------------------------------------------------------------
        )                        end of look-ahead
      --------------------------------------------------------------------------------
        (?<!                     look behind to see if there is not:
      --------------------------------------------------------------------------------
          \1                       what was matched by capture \1
      --------------------------------------------------------------------------------
          .                        any character except \n
      --------------------------------------------------------------------------------
          .                        any character except \n
      --------------------------------------------------------------------------------
        )                        end of look-behind
      

      【讨论】:

        最近更新 更多