正则表达式匹配两组重复数字,其中两者不允许是相同的数字

regex matching two groups of repeating digits where both are not allowed to be the same digits

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

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

ABBBCCDD 

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

作为此匹配的一部分,我一直在使用具有否定前瞻性的正则表达式,它有效但并非一直有效,我对原因感到困惑。我希望有人可以解释为什么会出现故障并提出解决方案。

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

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

分解这个..

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

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

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

I 运行 在 Python 中检查此表达式并在 PCRE 模式下使用 grep (-P)。两者都匹配了错误的模式。

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

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

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

将 DD 更改为 11,复制 BBB,我们还发现尽管 B 有前向否定检查,但它仍然通过了..

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

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

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

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

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

返回原来的测试号码并将 CC 数字切换为与 B 相同的“1”。它没有通过。

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

要为 BBB 组播放此内容,请将 B 数字设置为与 A 遇到的相同的 0。也无法匹配..

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

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

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

所以在我看来,前向否定检查是有效的,但它只适用于下一个相邻的集合或其预期的先行 运行ge 以某种形式被缩短,大概是由于我们额外的东西'正在尝试匹配。

如果我在 B 之后立即在 A 上添加额外的前瞻性并且它的重复已经被处理,我们可以避免在 CC 部分匹配重用 A 数字..

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

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

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

(.)(?!.{0,6})(.){2}(?!{1,4})(.){1}(?!{1,2})(.){1}

   ^^^^^^^^

lookahead 更改为当 出现在 string.See 中的任何位置时禁止匹配 demo.You 可以类似地修改正则表达式中的其他部分。

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

注意:已更新。

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

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

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

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

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

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

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

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

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

根据目前的反馈,我给出了另一个答案,它不依赖于基于总长度的算术运算,而是独立地识别长度中 4 个唯一 character/digit 组的任何序列字符串中任意位置的序列 1,3,2,2:

/(?<=^|(.)(?!))(.){0}(?!)(.){2}(?!|)(.){1}(?!||)(.){1}(?!)/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 {0} is superfluous
                         ^^^^^^ this ensures the next group is unique
                               ^^^^^^^^ this is the size-3 group
etc.

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

这里只是一些关于我的最终解决方案的细节..

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

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

结果是..

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

因此,一旦捕获了 \1 (A),我就测试了 A 之前的 0-6 个通配符的负前瞻。然后我捕获了 \2 (B),它的两次重复,然后为 B 提供了 0-3 个通配符的负前瞻+ B 等等。

它使注意力集中在消极地向前看,以确保被捕获的群体不会重复他们不应该重复的地方。然后后续捕获及其重复模式将完成剩下的工作以确保匹配。

最后一组的其他例子:

ABCCDDDD
(.)(?!.{0,6})(.)(?!.{0,5})(.){1}(?!.{0,3})(.){3}

AABBCCDD
(.){1}(?!.{0,5})(.){1}(?!.{0,3})(.){1}(?!.{0,1})(.){1}

ABCCDEDE
09(.)(?!.{0,6})(.)(?!.{0,5})(.){1}(?!.{0,3})(.)(?!{1})(.){1}{1}