Ответ 1
Я не думаю, что это возможно в аромате регулярного выражения Python. Вам понадобится рекурсия (или, скорее, повторное использование шаблонов), которая поддерживается только PCRE. Фактически, PCRE даже упоминает, как определяющие сокращения работают в своей странице man (поиск "Определение подшаблонов" ).
В PCRE вы можете использовать синтаксис рекурсии аналогично обратным ссылкам - за исключением того, что шаблон применяется снова, вместо того, чтобы пытаться получить тот же литеральный текст, что и от обратной ссылки. Пример:
/(\d\d)-(?1)-(?1)/
Совпадает с чем-то вроде даты (где (?1)
будет заменено на \d\d
и снова оценено). Это действительно мощно, потому что, если вы используете эту конструкцию в самой ссылочной группе, вы получаете рекурсию, но нам здесь даже не нужно. Вышеупомянутое также работает с именованными группами:
/(?<my>\d\d)-(?&my)-(?&my)/
Теперь мы уже очень близки, но это определение также является первым использованием шаблона, который несколько загромождает выражение. Хитрость заключается в том, чтобы использовать шаблон сначала в позиции, которая никогда не оценивается. Страницы руководства предполагают, что условие зависит от (несуществующей) группы DEFINE
:
/
(?(DEFINE)
(?<my>\d\d)
)
(?&my)-(?&my)-(?&my)
/x
Конструкция (?(group)true|false)
применяет шаблон true
, если раньше использовалась группа group
, и (необязательный) шаблон false
в противном случае. Поскольку нет группы DEFINE
, условие всегда будет ложным, а шаблон true
будет пропущен. Следовательно, мы можем поставить там всевозможные определения, не опасаясь, что они когда-либо будут применены и испортят наши результаты. Таким образом, мы получаем их в шаблон, не используя их.
И альтернатива - это негативный взгляд, который никогда не достигает точки, в которой определяется выражение:
/
(?!
(?!) # fail - this makes the surrounding lookahead pass unconditionally
# the engine never gets here; now we can write down our definitions
(?<my>\d\d)
)
(?&my)-(?&my)-(?&my)
/x
Однако вам действительно нужна эта форма, если у вас нет условных выражений, но у меня есть повторное использование шаблонов (и я не думаю, что такой аромат существует). Другой вариант имеет то преимущество, что использование DEFINE
делает очевидным, для чего предназначена группа, в то время как подход lookahead немного запутан.
Итак, вернемся к вашему оригинальному примеру:
/
# Definitions
(?(DEFINE)
(?<BEGIN>[$][(])
)
# And now your pattern
(?: (?&BEGIN) ( [^)]+ ) \) ) # the field access specifier
|
(
(?: # any one single character before the '$('
\n | . (?= (?&BEGIN) )
)
|
(?: # any one single character, except the one before the '$('
\n | . (?! (?&BEGIN) )
)*
)
/x
Для этого подхода есть два основных оговорки:
- Рекурсивные ссылки atomic. То есть, как только ссылка сопоставит что-то, она никогда не будет возвращена. В некоторых случаях это может означать, что вы должны быть немного умны в создании своего выражения, так что первое совпадение всегда будет тем, которое вы хотите.
- Вы не можете использовать захват внутри определенных шаблонов. Если вы используете что-то вроде
(?<myPattern>a(b)c)
и повторно его используете,b
никогда не будет захвачен - при повторном использовании шаблона все группы не захватываются.
Самое важное преимущество перед любым типом интерполяции или конкатенации состоит в том, что вы никогда не сможете создавать недопустимые шаблоны с этим, и вы не можете испортить количество ваших захваченных групп.