CascadiaPHP 2024

Условные подшаблоны

В модуле PCRE реализована возможность подчинять шаблон условию или выбирать из двух условных подшаблонов, в зависимости от успеха сопоставления предыдущего подшаблона. У условных подшаблонов две допустимые формы:

(?(condition)yes-pattern)
(?(condition)yes-pattern|no-pattern)

При успешном сопоставлении условия condition будет выбран подшаблон yes-pattern, иначе — no-pattern, если он есть. Если указали более двух альтернатив, парсер выдаст ошибку времени компиляции.

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

( \( )?    [^()]+    (?(1) \) )

Первая часть соответствует опциональной открывающей скобке, и в случае если она присутствует, захватывает её как значение первого подшаблона. Следующая часть соответствует одному или более символам, которые отличаются от круглой скобки. Третья часть — условный подшаблон, который зависит от результата сопоставления первого подшаблона. Если в начале обрабатываемых данных парсер обнаружил открывающую круглую скобку, условие будет интерпретировано как истина, и, следовательно, для успешного сопоставления третьей части шаблона необходима закрывающая круглая скобка. В противном случае, поскольку вторую ветвь условного шаблона не указали, парсер сопоставит третью часть с пустой строкой. Суммируя все вышесказанное, приведённый шаблон совпадает с последовательностью не-скобок, возможно, заключённой в круглые скобки.

Если условие — строка (R), парсер выполнит условие, если будет произведён рекурсивный вызов к шаблону или подшаблону. На «самом верхнем уровне» условие ложно.

Если условие не является последовательностью цифр или (R), оно должно быть утверждением. Это может быть либо положительная или отрицательная проверка последующего либо предыдущего текста. Рассмотрим данный шаблон, снова содержащий незначащие пробелы, с двумя альтернативами на второй строке:

(?(?=[^a-z]*[a-z])
\d{2}-[a-z]{3}-\d{2}  |  \d{2}-\d{2}-\d{2} )

Приведён пример с утверждающим условием касательно предшествующего текста, которое выполняется для необязательной последовательности не-букв с последующей буквой. Говоря другими словами, указанное условие проверяет наличие хотя бы одной предшествующей буквы. Если буква найдена, выполняется сопоставление с первой альтернативой, в противном случае — со второй альтернативой. Приведённый шаблон соответствует строкам двух видов: dd-aaa-dd либо dd-dd-dd, где aaaa — это буквы, а dd — цифры.

add a note

User Contributed Notes 1 note

up
3
Anonymous
13 years ago
Repetition of a subpattern will repeat conditionals that are contained inside it, updating subpattern matches with iteration.

Consider the following code, which scans thru HTML, keeping track of angle brackets "<" ">". If open bracket "<" matches, then closing bracket ">" must follow before repetition can possibly end. That way regex will effectively match only outside of tags.

<?php
$pattern
='%(*ANY)(.*?(<)(?(2).*?>)(.*?))*?\'\'%s';
$replace='\1Fred';
$subject=
'<html><body class=\'\'>\'\' went to '\'\meyer and ran
into <b>\'\'</b>.
</body></html>'
echo preg_replace("%(*ANY)(.*?((<)(?(3).*?>).*?)*?)\'\'%s",'\1Fred',$subject);
?>

Output will be:
'<html><body class=\'\'>Fred went to Fredmeyer and ran
into <b>Fred</b>.
</body></html>'
To Top