Ответ 1
Я не знаю механизм регулярных выражений, который может захватить квантификатор. Тем не менее, с помощью PCRE или Perl можно использовать некоторые трюки, чтобы проверить, есть ли у вас одинаковое количество символов. В вашем примере:
@@@@ "Star Wars" ==== "1977" ---- "Science Fiction" //// "George Lucas"
вы можете проверить, сбалансированы ли
@
=
-
/
с этим шаблоном, который использует знаменитый трюк Qtax, (готовы ли вы?): "собственнически-необязательная группа саморегуляции"
~(?<[email protected])((?:@(?=[^=]*(\2?+=)[^-]*(\3?+-)[^/]*(\4?+/)))+)([email protected])(?=[^=]*\2(?!=)[^-]*\3(?!-)[^/]*\4(?!/))~
подробнее:
~ # pattern delimiter
(?<[email protected]) # negative lookbehind used as an @ boundary
( # first capturing group for the @
(?:
@ # one @
(?= # checks that each @ is followed by the same number
# of = - /
[^=]* # all that is not an =
(\2?+=) # The possessive optional self-referencing group:
# capture group 2: backreference to itself + one =
[^-]*(\3?+-) # the same for -
[^/]*(\4?+/) # the same for /
) # close the lookahead
)+ # close the non-capturing group and repeat
) # close the first capturing group
([email protected]) # negative lookahead used as an @ boundary too.
# this checks the boundaries for all groups
(?=[^=]*\2(?!=)[^-]*\3(?!-)[^/]*\4(?!/))
~
Основная идея
Группа, не участвующая в записи, содержит только один @
. Каждый раз, когда эта группа повторяется, в группы захвата 2, 3 и 4 добавляется новый символ.
собственнически-необязательная самореферентная группа
Как это работает?
( (?: @ (?= [^=]* (\2?+ = ) .....) )+ )
При первом появлении символа @группа захвата 2 еще не определена, поэтому вы не можете написать что-то вроде этого (\2 =)
, которое приведет к сбою шаблона. Чтобы избежать этой проблемы, нужно сделать необязательным backreference: \2?
Второй аспект этой группы состоит в том, что количество совпадающих символов =
увеличивается при каждом повторении группы без захвата, так как каждый раз добавляется =
. Чтобы гарантировать, что это число всегда увеличивается (или сбой шаблона), притяжательный квантификатор заставляет обратную ссылку сначала сопоставляться перед добавлением нового символа =
.
Обратите внимание, что эта группа может быть видна следующим образом: , если существует группа 2, а затем сопоставить ее со следующим =
( (?(2)\2) = )
Рекурсивный путь
~(?<[email protected])(?=(@(?>[^@=]+|(?-1))*=)(?!=))(?=(@(?>[^@-]+|(?-1))*-)(?!-))(?=(@(?>[^@/]+|(?-1))*/)(?!/))~
Вам нужно использовать совпадающие совпадения, так как вы будете использовать часть @несколько раз, поэтому причина заключается в том, что весь шаблон находится внутри поисковых запросов.
подробнее:
(?<[email protected]) # left @ boundary
(?= # open a lookahead (to allow overlapped matches)
( # open a capturing group
@
(?> # open an atomic group
[^@=]+ # all that is not an @ or an =, one or more times
| # OR
(?-1) # recursion: the last defined capturing group (the current here)
)* # repeat zero or more the atomic group
= #
) # close the capture group
(?!=) # checks the = boundary
) # close the lookahead
(?=(@(?>[^@-]+|(?-1))*-)(?!-)) # the same for -
(?=(@(?>[^@/]+|(?-1))*/)(?!/)) # the same for /
Основное отличие от шаблона прецедента состоит в том, что это не заботится о порядке групп =
-
и /
. (Однако вы можете легко внести некоторые изменения в первый шаблон, чтобы справиться с этим, с классами символов и отрицательными образами.)
Примечание. Для примерной строки, если быть более конкретным, вы можете заменить отрицательный lookbehind на якорь (^
или \A
). И если вы хотите получить целую строку в качестве результата соответствия, вы должны добавить .*
в конец (иначе результат совпадения будет пустым, так как игривый замечает его).