Регулярное выражение, совпадающее с двумя группами повторяющихся цифр, где обе не могут быть одинаковыми цифрами
Ребята, я пытаюсь использовать регулярные выражения для обработки большого набора числовых строк и сопоставления последовательностей цифр для определенных шаблонов, где некоторые цифры повторяются в группах. Часть требования заключается в обеспечении уникальности между разделами данного шаблона.
Пример соответствия, которое я пытаюсь достичь
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
появляется в любом месте строки. См. демонстрацию. Вы также можете изменить другие части в своем регулярном выражении.
Основываясь на обратной связи, я даю другой ответ, который не зависит от выполнения арифметики на основе общей длины и который сам по себе идентифицирует любую последовательность из 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 находится в поле зрения.