【问题标题】:regex matching two groups of repeating digits where both are not allowed to be the same digits正则表达式匹配两组重复数字,其中不允许两者是相同的数字
【发布时间】:2015-09-29 11:20:48
【问题描述】:

各位, 我正在尝试使用正则表达式来处理大量数字字符串并匹配特定模式的数字序列,其中某些数字在组中重复。部分要求是确保给定模式的各个部分之间的唯一性。

我正在尝试实现的匹配示例

ABBBCCDD 

将其解释为一组数字。但是 A,B,C,D 不能相同。每个的重复是我们试图匹配的模式。

我一直在使用带有负前瞻的正则表达式作为此匹配的一部分,它有效,但并非一直有效,我不知道为什么。我希望有人能解释为什么它会出现故障并提出解决方案。

因此,为了解决 ABBBCCDD,我提出了这个 RE,使用了使用组的负前瞻..

(.)(?!\1{1,7})(.)\2{2}(?!\2{1,4})(.)\3{1}(?!\3{1,2})(.)\4{1}

分解这个..

(.)           single character wildcard group 1 (A)
(?!\1{1,7})   negative look-ahead for 1-7 occurrences of group 1 (A)
(.)           single character wildcard group 2 (B)
\2{2}         A further two occurrences of group 2 (B)
(?!\2{1,4})   Negative look-ahead of 1-4 occurrences of group 2 (B)
(.)           single character wildcard group 3 (C)
\3{1}         One more occurrence of group 3 (C)
(?!\3{1,2})   Negative look-ahead of 1-2 occurrences of group 3 (C)
(.)           single character wildcard group 4 (D)
\4{1}         one more occurrence of group 4 (D)

这里的想法是,负前瞻是一种验证给定字符是否未在意外位置找到的方法。所以 A 在接下来的 7 个字符中得到检查。一旦 B 和它的 2 次重复匹配,我们就会在接下来的 4 个字符中消极地寻找 B。最后,一旦这对 C 匹配,我们就会在最后的 2 个中寻找一个 C 作为检测不匹配的方法。

对于测试数据,此字符串“01110033”与表达式匹配。但这不应该是因为 A 的“0”在 C 位置重复。

我在 Python 中检查了这个表达式,并在 PCRE 模式下使用 grep (-P)。两者都匹配了错误的模式。

我将表达式与相同的测试字符串“01110033”一起放入https://regex101.com/,它也在那里匹配。我没有足够的评级来发布这个或我用测试数据尝试过的变体的图像。所以这里有一些使用 grep -P 命令行运行的文本抓取

所以我们在 CC 位置重复 A 的无效表达式通过了..

$ echo "01110033" | grep -P '(.)(?!\1{1,7})(.)\2{2}(?!\2{1,4})(.)\3{1}(?!\3{1,2})(.)\4{1}'
01110033
$

将 DD 更改为 11,复制 BBB,我们也发现尽管 B 有一个正向否定检查,但仍然可以通过..

$ echo "01110011" | grep -P '(.)(?!\1{1,7})(.)\2{2}(?!\2{1,4})(.)\3{1}(?!\3{1,2})(.)\4{1}'
01110011
$

现在将 DD 更改为“00”,复制 CC 数字和低位,结果不匹配..

$ echo "01110000" | grep -P '(.)(?!\1{1,7})(.)\2{2}(?!\2{1,4})(.)\3{1}(?!\3{1,2})(.)\4{1}'
$

从表达式中删除对 CC "(?!\3{1,2})" 的正负检查,我们在 D 位置重复 C 数字使其通过。

$ echo "01110000" | grep -P '(.)(?!\1{1,7})(.)\2{2}(?!\2{1,4})(.)\3{1}(.)\4{1}'
01110000
$

回到原来的测试号,把CC数字换成和B一样用的“1”,打不通。

$ echo "01111133" | grep -P '(.)(?!\1{1,7})(.)\2{2}(?!\2{1,4})(.)\3{1}(?!\3{1,2})(.)\4{1}'
$

要为 BBB 组执行此操作,请将 B 数字设置为与 A 遇到的相同的 0。也无法匹配..

$ echo "00002233" | grep -P '(.)(?!\1{1,7})(.)\2{2}(?!\2{1,4})(.)\3{1}(?!\3{1,2})(.)\4{1}'
$ 

然后取出 A 的负前瞻,我们可以匹配..

$ echo "00002233" | grep -P '(.)(.)\2{2}(?!\2{1,4})(.)\3{1}(?!\3{1,2})(.)\4{1}'
00002233
$ 

所以在我看来,前向否定检查是有效的,但它只适用于下一个相邻的集合,或者它的预期超前范围以某种形式被缩短,大概是由于我们试图匹配的额外事物。

如果我在 B 之后立即在 A 上添加一个额外的前瞻,并且它的重复已被处理,我们会得到它以避免在 CC 部分上重复使用 A 数字进行匹配..

$ echo "01110033" | grep -P '(.)(?!\1{1,7})(.)\2{2}(?!\1{1,4})(?!\2{1,4})(.)\3{1}(?!\3{1,2})(.)\4{1}'
$

为了更进一步,在匹配 CC 集之后,我需要再次对 A 和 B 重复负前瞻。这似乎是错误的。

希望 RE 专家可以澄清我在这里做错了什么,或者根据我所观察到的情况确认负前瞻是否确实受到限制

【问题讨论】:

    标签: regex negative-lookahead


    【解决方案1】:
    (.)(?!.{0,6}\1)(.)\2{2}(?!\2{1,4})(.)\3{1}(?!\3{1,2})(.)\4{1}
    
       ^^^^^^^^
    

    \1 出现在字符串中的任何位置时,更改您的lookahead 以禁止匹配。参见演示。您也可以在您的正则表达式中类似地修改其他部分。

    https://regex101.com/r/vV1wW6/31

    【讨论】:

    • 谢谢。这将特别适用于我展示的示例。然而,一些更复杂的模式将在稍后的序列中重复 A、B、C、D。但是根据您的回复,我刚刚发现使用 ..(?!.{0,6}\1) 也可以作为在接下来的 7 个字符中测试我的组的一种方法,方法是将它作为 0-6 通配符,然后是我的组字符。所以我现在将采用这种方法。再次感谢您的快速响应。
    【解决方案2】:

    注意:已更新。

    正如 vks 已经指出的那样,您的负面预测并没有排除您的想法——例如,\1{1,7} 只会排除 A、AA、AAA、AAAA、AAAA、AAAAAA 和 AAAAAAA。我认为您希望前瞻为.*\1.*\2.*\3 等。

    但这里有另一个想法:很容易预先过滤掉任何包含不相邻重复字符的行:

    grep -P -v '(.)(?!\1).*\1'
    

    然后你的正则表达式就更简单了:.{1}.{3}.{2}.{2}

    事实上,整个事情可以使用第一个作为负前瞻约束来组合:

    (?!.*(.)(?!\1).*\1).{1}.{3}.{2}.{2}
    

    或者,如果您需要像最初那样捕获数字:

    (?!.*(.)(?!\1).*\1)(.){1}(.){3}(.){2}(.){2}
    

    但请注意,这些数字现在将是 \2 \3 \4 \5,因为 \1 在前瞻中。

    【讨论】:

    • 感谢您的澄清。我需要匹配一组超过 50 种模式,它们与模式遍布各处。在某些情况下,相同的字符会进一步重复。所以我最终不得不将 vks 的解决方案修改为 (?!.{0,6}\1) 这将测试 0-6 个荒野,然后是 \1 作为我认为 \1{1,7} 的替代品应该为我做的。然后其他表达式就变成了相同技巧的变体
    • 不客气。就是这样——完整问题空间的轮廓是您/我们在获得任何一般正确解决方案之前所需要的。如果它真的是“到处都是模式”,那么不存在通用的解决方案——你必须零碎地做。
    【解决方案3】:

    根据迄今为止的反馈,我给出了另一个答案,它不依赖于基于总长度进行算术运算,并且将独立识别长度序列 1,3 中 4 个唯一字符/数字组的任何序列,2,2 字符串中的任意位置:

    /(?<=^|(.)(?!\1))(.)\2{0}(?!\2)(.)\3{2}(?!\2|\3)(.)\4{1}(?!\2|\3|\4)(.)\5{1}(?!\5)/gm
     ^^^^^^^^^^^^^^^^ this is a look-behind that makes sure we're starting with a new character/digit
                     ^^^^^^^^ this is the size-1 group; yes the \2{0} is superfluous
                             ^^^^^^ this ensures the next group is unique
                                   ^^^^^^^^ this is the size-3 group
    etc.
    

    如果这更接近您的解决方案,请告诉我。如果是这样,并且如果您的所有“模式”都包含您正在寻找的组大小的序列(如 1、3、2、2),我可以想出一些代码来为任何此类生成相应的正则表达式输入“模式”。

    【讨论】:

      【解决方案4】:

      这里只是一些关于最终解决方案对我来说是什么样子的细节..

      所以从根本上说 (?!\1{1,7}) 并不是我所想的那样,并且是我遇到问题的全部原因。衷心感谢你们为我找到了这个问题。

      我展示的示例是大约 50 个中的一个,我必须从一组模式中制定出来。

      结果……

      ABBBCCDD
      09(.)(?!.{0,6}\1)(.)\2{2}(?!.{0,3}\2)(.)\3{1}(?!.{0,1}\3)(.)\4{1}
      

      所以一旦 \1 (A) 被捕获,我测试了 A 之前 0-6 个通配符的负前瞻。然后我捕获 \2 (B),它的两次重复,然后给 B 0-3 个通配符 + B 的负前瞻等等。

      它将焦点集中在消极地向前看,以确保被捕获的群体不会重复他们不应该重复的地方。然后后续的捕获和它们的重复模式将完成其余的工作以确保匹配。

      最后一组的其他例子:

      ABCCDDDD
      (.)(?!.{0,6}\1)(.)(?!.{0,5}\2)(.)\3{1}(?!.{0,3}\3)(.)\4{3}
      
      AABBCCDD
      (.)\1{1}(?!.{0,5}\1)(.)\2{1}(?!.{0,3}\2)(.)\3{1}(?!.{0,1}\3)(.)\4{1}
      
      ABCCDEDE
      09(.)(?!.{0,6}\1)(.)(?!.{0,5}\2)(.)\3{1}(?!.{0,3}\3)(.)(?!\4{1})(.)\4{1}\5{1}
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2022-06-15
        • 1970-01-01
        • 1970-01-01
        • 2022-10-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-10-14
        相关资源
        最近更新 更多