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

标签 regex negative-lookahead

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

我试图实现的匹配类型的一个例子

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 模式(-P)下使用了 grep。两者都匹配错误的模式。

我把表达式放在 https://regex101.com/以及相同的测试字符串“01110033”,它也在那里匹配。我没有足够的评级来张贴我用测试数据尝试的这个或变体的图像。所以这里有一些从命令行运行中抓取的文本 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 专家可以澄清我在这里做错了什么,或者根据我观察到的情况确认负前瞻是否确实受到限制

最佳答案

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

   ^^^^^^^^

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

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

关于正则表达式匹配两组重复数字,其中不允许是相同的数字,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32842583/

相关文章:

JavaScript 正则表达式只选择最后一次出现

javascript - 格式化小写字符串以将每个句子的开头大写

python - 如果我从文件中读取字符串,则正则表达式不起作用

Python 正则表达式替换引号中的文本,引号本身除外

Java正则表达式

regex - 有没有办法在 vim 正则表达式中进行负前瞻?

regex - Perl 中带有 * 修饰符的否定前瞻断言

java - 正则表达式 : capture group NOT followed by

C# 正则表达式 : negative lookahead fails with the single line option