Регулярное выражение, совпадающее с двумя группами повторяющихся цифр, где обе не могут быть одинаковыми цифрами

Ребята, я пытаюсь использовать регулярные выражения для обработки большого набора числовых строк и сопоставления последовательностей цифр для определенных шаблонов, где некоторые цифры повторяются в группах. Часть требования заключается в обеспечении уникальности между разделами данного шаблона.

Пример соответствия, которое я пытаюсь достичь

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)

Мысль здесь заключается в том, что негативные прогнозные действия служат средством проверки того, что данный персонаж не найден там, где он неожидан. Таким образом, А проверяется в следующих 7 символов. Как только B и его 2 повторения совпали, мы негативно ожидаем B в следующих 4-х символах. Наконец, как только пара Cs сопоставлена, мы ищем в последних 2 C для средства обнаружения несоответствия.

Для тестовых данных эта строка "01110033" соответствует выражению. Но это не должно происходить, потому что "0" для A повторяется в позиции C.

Я запустил проверки этого выражения в Python и с grep в режиме PCRE (-P). Оба соответствовали неправильному образцу.

Я поместил выражение в https://regex101.com/ вместе с той же тестовой строкой "01110033", и оно также там соответствовало. У меня недостаточно рейтинга, чтобы опубликовать изображения этого или вариаций, которые я пробовал с тестовыми данными. Итак, вот некоторые текстовые захваты из командной строки с помощью команды grep -P

Таким образом, наше неверное выражение, которое повторяет A в позиции CC, проходит через..

$ 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 и low, и вот оно не совпадает..

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

Удалите прямую отрицательную проверку для CC "(?!\3{1,2})" из выражения, и наш повтор цифры C в позиции D делает это.

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

Вернитесь к исходному тестовому номеру и переключите цифры CC на то же использование "1" из B. Это не проходит.

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

И чтобы разыграть это для группы BBB, установите цифры B на те же 0, что и для A. Также не соответствует..

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

Тогда возьмите отрицательный взгляд на А, и мы сможем это сопоставить.

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

Поэтому мне кажется, что прямая отрицательная проверка работает, но она работает только со следующим смежным набором, или его предполагаемый диапазон прогнозирования в некоторой форме обрывается, предположительно, дополнительными вещами, которые мы пытаемся сопоставить.

Если я добавлю дополнительный обзор A сразу после B, и его повторение будет обработано, мы получим его, чтобы избежать совпадения в части 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 сможет уточнить, что я здесь делаю неправильно, или подтвердить, действительно ли негативные взгляды действительно ограничены на основании того, что я наблюдаю

4 ответа

Решение
(.)(?!.{0,6}\1)(.)\2{2}(?!\2{1,4})(.)\3{1}(?!\3{1,2})(.)\4{1}

   ^^^^^^^^

Измени свой lookahead запретить матч, когда \1 появляется в любом месте строки. См. демонстрацию. Вы также можете изменить другие части в своем регулярном выражении.

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

Основываясь на обратной связи, я даю другой ответ, который не зависит от выполнения арифметики на основе общей длины и который сам по себе идентифицирует любую последовательность из 4 уникальных групп символов / цифр в последовательности длины 1,3,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), я могу придумать некоторый код, который сгенерирует соответствующее регулярное выражение для любого такого введите "шаблон".

Вот лишь некоторые подробности о том, как окончательное решение выглядело для меня..

Таким образом, принципиально (?!\1{1,7}) было не то, что я думал, и это была полная причина проблем, с которыми я столкнулся. Ребята, искренне благодарны за то, что нашли для меня этот вопрос.

Пример, который я показал, был 1 из 50, которые я должен был сформулировать из набора шаблонов.

Это закончилось как..

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

Поэтому, как только \1 (A) был захвачен, я проверил отрицательный прогноз с 0-6 подстановочными символами, предшествующими A. Затем я захватил \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}

ПРИМЕЧАНИЕ: обновлено.

Как уже отмечалось, ваши негативные взгляды не исключают того, что вы подумали - \1{1,7} например, собирается только исключить A, AA, AAA, AAAA, AAAAA, 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 находится в поле зрения.

Другие вопросы по тегам