【问题标题】:regex to find a pair of adjacent digits with different digits around them正则表达式找到一对相邻的数字,它们周围有不同的数字
【发布时间】:2020-10-10 00:19:27
【问题描述】:

我是正则表达式的初学者,我正在尝试制作一个表达式来查找是否有两个相同的数字彼此相邻,并且这对数字后面和前面的数字不同。

例如,

123456678 应该匹配,因为有双 6,

1234566678 不应匹配,因为没有具有不同周围数字的双精度数。 12334566 应该匹配,因为有两个 3。

到目前为止,我有这个仅适用于 1,并且只要双精度不在字符串的开头或结尾,但是我可以通过在开头和结尾添加一个字母来处理它。

^.*([^1]11[^1]).*$

我知道我可以使用 [0-9] 而不是 1,但问题是它们都是同一个数字。

谢谢!

【问题讨论】:

  • 在 SO 上发布的第一个问题对您来说还不错。 (大多数似乎只收集反对票。)它被赞成是因为读者觉得它很有趣,尤其是明确说明。对未来的一些建议:首先用语言准确地陈述问题,然后给出说明性的例子,包括每个期望的结果(你已经完成了)。然后才显示您尝试过的代码(您已经完成了)。

标签: python python-3.x regex


【解决方案1】:

我的答案分为四个部分。

第一部分包含我对问题的解决方案。对其他内容不感兴趣的读者可以跳过其他部分。

其余三个部分关注的是识别前面有不同数字和后面有不同数字的相等数字对。三个部分中的第一个与它们匹配;另外两个将他们抓成一组。

我加入了最后一部分,因为我想与不熟悉它的人分享有史以来最伟大的正则表达式技巧,因为我觉得它非常酷、聪明,而且简单。它记录在here。请注意,为了制造悬念,该链接的作者在鼓声揭晓之前包含了一个冗长的序言。

判断一个字符串是否包含两个连续相等的数字,前面有一个不同的数字,后面有一个不同的数字

您可以按如下方式测试字符串:

import re

r = r'(\d)(?!\1)(\d)\2(?!\2)\d'
arr = ["123456678", "1123455a666788"]
for s in arr:
  print(s, bool(re.search(r, s)) )

展示

123456678 True
1123455a666788 False

Run Python code | Start your engine!1

正则表达式引擎执行以下操作。

(\d)    : match a digit and save to capture group 1 (preceding digit)
(?!\1)  : next character cannot equal content of capture group 1
(\d)    : match a digit in capture group 2 (first digit of pair)
\2      : match content of capture group 2 (second digit of pair)
(?!\2)  : next character cannot equal content of capture group 2
\d      : match a digit

(?!\1)(?!\2)负前瞻

使用 Python 的 regex 模块来匹配具有所需属性的连续数字对

您可以将以下正则表达式与 Python 的 regex 模块一起使用来获取匹配的数字对。

r'(\d)(?!\1)\K(\d)\2(?=\d)(?!\2)'

Regex Engine

正则表达式引擎执行以下操作。

(\d)    : match a digit and save to capture group 1 (preceding digit)
(?!\1)  : next character cannot equal content of capture group 1
\K      : forget everything matched so far and reset start of match
(\d)    : match a digit in capture group 2 (first digit of pair)
\2      : match content of capture group 2 (second digit of pair)
(?=\d)  : next character must be a digit
(?!\2)  : next character cannot equal content of capture group 2

(?=\d) 是一个正向预测(?=\d)(?!\2) 可以替换为 (?!\2|$|\D)

将具有所需属性的连续数字对保存到捕获组中

另一种获取匹配数字对的方法,不需要regex模块,是从以下正则表达式的匹配中提取捕获组2的内容。

r'(\d)(?!\1)((\d)\3)(?!\3)(?=\d)'

Re engine

执行以下操作。

(\d)    : match a digit in capture group 1
(?!\1)  : next character does not equal last character
(       : begin capture group 2
  (\d)  : match a digit in capture group 3
  \3    : match the content of capture group 3
)       : end capture group 2
(?!\3)  : next character does not equal last character
(?=\d)  : next character is a digit

使用 有史以来最伟大的正则表达式技巧来识别具有所需属性的连续数字对

我们使用下面的正则表达式来匹配字符串。

r'(\d)(?=\1)|\d(?=(\d)(?!\2))|\d(?=\d(\d)\3)|\d(?=(\d{2})\d)'

当有匹配时,我们不注意匹配的是哪个字符,而是检查捕获组 4 ((\d{2})) 的内容,如下所述。

The Trick in action

交替的前三个组成部分对应于四位数字的字符串不能具有第二个和第三个数字相等,第一个和第二个不相等,第三个和第四个相等的属性的方式。它们是:

(\d)(?=\1)        : assert first and second digits are equal    
\d(?=(\d)(?!\2))  : assert second and third digits are not equal
\d(?=\d(\d)\3)    : assert third and fourth digits are equal

因此,如果有一个数字匹配并且交替的前三个部分失败,则最后一部分 (\d(?=(\d{2})\d)) 必须成功,并且它包含的捕获组 (#4) 必须包含两个相等的数字具有所需的属性。 (需要最后的\d 来断言感兴趣的数字对后面跟着一个数字。)

如果有匹配,我们如何确定交替的最后一部分是否是匹配的?

当这个正则表达式匹配一个数字时,我们对那个数字不感兴趣。相反,我们希望捕获第 4 组 ((\d{2}))。如果该组为空,我们得出结论,交替的前三个组成部分之一与数字匹配,这意味着匹配数字后面的两个数字不具有它们相等且不等于它们之前和之后的数字的属性.

但是,如果捕获组 4 不为空,则表示交替的前三个部分中没有一个与数字匹配,因此交替的最后一部分必须匹配并且匹配数字后面的两个数字,这被保存在捕获组 4 中,具有所需的属性。

1.移动光标查看详细说明。

【讨论】:

    【解决方案2】:

    使用正则表达式,将 PyPi regex 模块与基于 (*SKIP)(*FAIL) 的模式一起使用会更加方便:

    import regex
    rx = r'(\d)\1{2,}(*SKIP)(*F)|(\d)\2'
    l = ["123456678", "1234566678"]
    for s in l:
      print(s, bool(regex.search(rx, s)) )
    

    请参阅Python demo。输出:

    123456678 True
    1234566678 False
    

    正则表达式详细信息

    • (\d)\1{2,}(*SKIP)(*F) - 一个数字,然后出现两次或多次相同的数字
    • | - 或
    • (\d)\2 - 一个数字,然后是同一个数字。

    重点是匹配所有3个或更多相同数字的块并跳过它们,然后匹配两个相同数字的块。

    请参阅regex demo

    【讨论】:

    • 这匹配 566a665566。请注意问题状态,“并且这对数字前后的数字不同”。
    • 但是,由于它适用于 OP,我假设像 22 这样的字符串必须是有效的,因为在 22 的两端没有额外的 2 字符。
    【解决方案3】:

    受答案或 Wiktor Stribiżew 的启发,使用与 re 交替使用的另一种变体是检查捕获组是否存在,该捕获组包含未被相同数字包围的 2 个相同数字的正匹配。

    在这种情况下,请检查第 3 组。

    ((\d)\2{2,})|\d(\d)\3(?!\3)\d
    

    Regex demo | Python demo

    • ( 捕获第 1 组
      • (\d)\2{2,} 捕获第 2 组,匹配 1 个数字并重复相同的数字 2 次以上
    • )关闭群
    • |或者
    • \d(\d) 匹配一个数字,在组 3 中捕获一个数字
    • \3(?!\3)\d 匹配第 3 组中的相同数字。匹配第 4th 数字,但不应与第 3 组数字相同

    例如

    import re
    
    pattern = r"((\d)\2{2,})|\d(\d)\3(?!\3)\d"
    strings = ["123456678", "12334566", "12345654554888", "1221", "1234566678", "1222", "2221", "66", "122", "221", "111"]
    
    for s in strings:
        match = re.search(pattern, s)
        if match and match.group(3):
            print ("Match: " + match.string)
        else:
            print ("No match: " + s)
    

    输出

    Match: 123456678
    Match: 12334566
    Match: 12345654554888
    Match: 1221
    No match: 1234566678
    No match: 1222
    No match: 2221
    No match: 66
    No match: 122
    No match: 221
    No match: 111
    

    如果例如只有 2 或 3 位数字也可以匹配,您可以检查第 2 组

    (\d)\1{2,}|(\d)\2
    

    Python demo

    【讨论】:

      【解决方案4】:

      你也可以用简单的方法。

      import re
      l=["123456678",
      "1234566678",
      "12334566 "]
      for i in l:
          matches = re.findall(r"((.)\2+)", i)
          if any(len(x[0])!=2 for x in matches):
              print "{}-->{}".format(i, False)
          else:
              print "{}-->{}".format(i, True)
      

      您可以根据自己的规则进行自定义。

      输出:

      123456678-->True
      1234566678-->False
      12334566 -->True
      

      【讨论】:

        猜你喜欢
        • 2020-08-20
        • 1970-01-01
        • 1970-01-01
        • 2021-12-13
        • 2019-05-29
        • 2013-05-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多