Сопоставьте ровно N повторений одного и того же символа

Как написать выражение, которое соответствует точно N повторениям одного и того же символа (или, в идеале, той же группы)? В основном, что (.)\1{N-1} , но с одним важным ограничением: выражение должно терпеть неудачу, если субъект повторяется более N раз. Например, учитывая N=4 и строку xxaaaayyybbbbbzzccccxx , выражения должны соответствовать aaaa и cccc а не bbbb .

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

Используйте негативный взгляд и негативный вид.

Это было бы регулярное выражение: (.)(?<!\1.)\1{N-1}(?!\1) за исключением того, что модуль python re нарушен (см. Эту ссылку ).

Английский перевод: «Сопоставьте любой символ. Удостоверьтесь, что после того, как вы совпадаете с этим символом, символ перед ним не является также этим символом. Сопоставьте N-1 с повторениями этого символа. Убедитесь, что символ после этих повторений не является также персонаж."

К сожалению, модуль re (и большинство движков регулярных выражений) сломан, поскольку вы не можете использовать обратные ссылки в утверждении lookbehind. Утверждения Lookbehind обязательны для постоянной длины, а компиляторы недостаточно интеллектуальны, чтобы сделать вывод, что это когда используется обратная ссылка (хотя, как и в данном случае, backref имеет постоянную длину). Мы должны передать компилятору регулярных выражений через это:

Фактический ответ должен быть более беспорядочным: r"(.)(?<!(?=\1)..)\1{N-1}(?!\1)"

Это работает вокруг этой ошибки в модуле re, используя (?=\1).. вместо \1. (обычно это эквивалентно.) Это позволяет механизму регулярного выражения точно знать ширину утверждения lookbehind, поэтому он работает в PCRE и re и т. д.


Конечно, решение в реальном мире – это что-то вроде [x.group() for x in re.finditer(r"(.)\1*", "xxaaaayyybbbbbzzccccxx") if len(x.group()) == 4]

Я подозреваю, что вы хотите использовать отрицательный lookahead: (.)\1{N-1}(?!\1) .

Но это сказало … Я подозреваю, что самое простое кросс-язычное решение просто записывает его самостоятельно, не используя регулярные выражения.

ОБНОВИТЬ:

^(.)\\1{3}(?!\\1)|(.)(?<!(?=\\2)..)\\2{3}(?!\\2) работает для меня в целом, включая совпадения, начинающиеся в начале строки.

Легко наложить слишком много бремени на регулярные выражения и попытаться заставить их делать все , когда почти все будет делать!

Используйте регулярное выражение для поиска всех подстрок, состоящих из одного символа, и затем проверяйте их длину отдельно, например:

 use strict; use warnings; my $str = 'xxaaaayyybbbbbzzccccxx'; while ( $str =~ /((.)\2*)/g ) { next unless length $1 == 4; my $substr = $1; print "$substr\n"; } 

вывод

 aaaa cccc