K3. Использование метасимволов

Обратная косая черта (\)

Символ обратной косой черты используется в нескольких случаях. Во-первых, если за ним следует небуквенно-цифровой символ, он отнимает у такого символа какое-либо специальное значение. Это употребление обратной косой черты в качестве управляющего символа допустимо как внутри, так и вне класса символов.

Например, если вы хотите задать соответствие символу *, вам необходимо указать в шаблоне \*. Независимо от того, интерпретируется ли следующий символ без \ как метасимвол, управляющий символ можно использовать перед небуквенно-цифровым символом «на всякий случай», чтобы указать, что он означает сам себя. В частности, для сопоставления с обратной косой чертой следует писать \\.

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

Если вы хотите убрать специальное значение последовательности символов, вы можете сделать это, расположив их между \Q и \E. Последовательность \Q...\E действует как внутри, так и вне класса символов.

Неотображаемые символы

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

\a

звонок, т.е. символ BEL (шестнадцатеричный 07),

\cx

"control-x", где x - любой символ,

\e

символ escape (шестнадцатеричный 1B),

\f

перевод страницы (шестнадцатеричный 0C),

\n

перевод строки (шестнадцатеричный 0A),

\r

возврат каретки (шестнадцатеричный 0D),

\t

табуляция (шестнадцатеричный 09),

\ddd

символ с восьмеричным кодом ddd или обратная ссылка,

\xhh

символ с шестнадцатеричным кодом hh.

Эффект применения \cx состоит в следующем: если x выражен строчной буквой, она преобразуется в заглавную. Затем инвер­тируется 6-й бит символа (шестнадцатеричный 40). Таким образом, \cz становится шестнадцатеричным 1A, но \c{ становится шестнадцатеричным 3B, в то время как \c; становится шестнадцатеричным 7B.

После \x читаются от нуля до двух шестнадцатеричных цифр (буквы могут быть заглавными или строчными).

После \0 читаются две следующие восьмеричные цифры. В обоих случаях, если цифр меньше двух, то используются только те, которые имеются.

Таким образом, последовательность \0\x\07 означает два двоичных нуля, за которыми следует символ BEL (значение кода 7). В случае если вы используете представление числа в восьмеричном коде, убедитесь, что за начальным нулем следуют две значащие цифры.

Обработка обратной косой черты, за которой следует цифра, отличная от 0, довольно сложна. Вне класса символов PCRE считывает ее и любые следующие за ней цифры как десятичное число. Если число меньше 10 или если ему в выражении предшествует столько же захватывающих левых скобок, вся последовательность рассматривается как обратная ссылка.

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

\040

альтернативный способ записи пробела,

\40

тоже самое, при условии, что данной записи предшествует менее 40 захватывающих подшаблонов,

\7

всегда обратная ссылка,

\11

может быть как обратной ссылкой, так и альтернативной записью символа табуляции,

\011

всегда обозначает символ табуляции,

\0113

символ табуляции, за которым следует символ "3",

\113

может быть обратной ссылкой, иначе - символ с восьмеричным кодом 113,

\377

может быть обратной ссылкой, иначе - байт, состоящий из единичных битов,

\81

может быть как обратной ссылкой, так и двоичным нулем, за которым следуют два символа "8" и "1".

Заметьте, что восьмеричные значения 100 или более не следует предварять нулями, так как более трех восьмеричных цифр никогда не считывается.

Все последовательности, определяющие значение единичного символа, могут использоваться как внутри, так и вне класса символов. Кроме того, внутри класса символов последовательность \b интерпретируется как символ возврата (backspace, шестнадцатеричный 08), а последовательность \X как символ "X". Вне класса символов эти последовательности имеют другие значения.

Типы родовых символов

Третий способ применения обратной косой черты - определение типов родовых символов. Следующие типы распознаются всегда:

\d

любая десятичная цифра,

\D

любой символ, кроме десятичной цифры,

\s

любой символ пробела (whitespace),

\S

любой символ, не являющийся символом пробела,

\w

любой алфавитно-цифровой символ и символ подчеркивания,

\W

любой символ, кроме цифр, букв и символов подчеркивания.

Каждая пара таких управляющих последовательностей делит полное множество всех символов на два непересекающихся множества. Любой символ соответствует одному и только одному множеству из пары.

Эти последовательности типов символов могут появляться как внутри, так и вне класса символов. Каждый из них соответствует одному символу определенного типа. Если текущее сопоставление находится в конце текста, поиск заканчивается неудачей, так как нет символа для сопоставления.

\s не соответствует символу VT (код 11). В этом состоит его отличие от класса "пробела" в POSIX. Символами \s являются HT (9), LF (10), FF (12), CR (13) и пробел (32).

Простые утверждения

Четвертое применение обратной косой черты - написание некоторых простых утверждений. Утверждение обозначает условие, которое должно быть соблюдено в определенной позиции при сопоставлении, не затрагивая каких-либо символов из строки темы. Использование подшаблона для более сложных утверждений описывается ниже.

Утверждения, сопровождаемые обратной косой чертой:

\b

сопоставление на границе слова,

\B

сопоставление не на границе слова,

\A

сопоставление на начале текста,

\Z

сопоставление на конце текста или перед символом перевода строки, расположенным в конце,

\z

сопоставление на конце текста,

\G

сопоставление на первой позиции поиска в тексте.

Такие утверждения не могут находиться внутри класса символов (но следует отметить, что \b внутри класса символов имеет иное значение – символ возврата (backspace)).

Диакритический знак (^) и символ доллара ($)

По умолчанию, вне класса символов метасимвол начала строки ^ является утверждением, справедливым только в случае, если текущая позиция поиска находится в начале строки текста. Внутри класса символов диакритический знак имеет совершенно иное значение (см. ниже).

Метасимвол ^ может и не быть первым символом в шаблоне, если используется несколько вариантов, но он должен быть на первом месте в каждом варианте, в котором он появляется, если предполагается, что шаблон должен соответствовать этой ветке. Если все возможные варианты начинаются с диакритического знака, то есть, если шаблон привязан к началу текста, говорят, что шаблон «заякорен». (Существуют и другие способы «заякорить» шаблон).

Символ доллара $ является утверждением, верным только если текущее совпадение находится в конце строки текста или непо­средственно перед символом перевода строки, являющимся последним символом в строке (по умолчанию). Не следует употреблять знак доллара в качестве последнего символа в шаблоне, если используется несколько альтернативных вариантов, но его следует поместить в конец каждой ветки, в которой он появляется. В классе символов у доллара нет специального значения.

Значения диакритического знака ^ и знака доллара $ изменяются, если установлена опция PCRE_MULTILINE. В этом случае, помимо сопоставления в начале и конце строки темы, они сопоставляются сразу после или непосредственно перед внутренним символом перевода строки соответственно. Например, в многострочном представлении шаблон /^abc$/ соответствует строке "def\nabc" (где \n- символ перевода строки), и не иначе. Следовательно, шаблоны, которые привязаны к концу или началу в однострочном представлении из-за того, что все ветви начинаются с ^, не являются привязанными в многострочном представлении, и сопоставление с диакритическим знаком возможно при ненулевом начальном смещении аргумента функции pcre_exec().

Точка

Вне класса символов точка в шаблоне соответствует любому символу в теме, включая неотображаемые символы, кроме (по умолчанию) символа перевода строки. Обработка точки совершенно не зависит от обработки диакритического знака ^ и символа доллара $, единственная связь между ними состоит в использовании символов перевода строки. У точки нет особого значения в классе символов.

Квадратные скобки и классы символов

Открывающая квадратная скобка вводит класс символов, завершаемый закрывающей квадратной скобкой. Закрывающая квадратная скобка сама по себе не имеет специального значения. Если закрывающая квадратная скобка требуется в качестве члена класса, она должна быть первым символом в классе (после начального диакритического знака, если таковой имеется) или экранироваться обратной косой чертой. Класс символов соответствует единственному символу в тексте.

Класс символов соответствует одиночному символу в теме. Искомый символ должен находиться в наборе символов, определенных классом, за исключением случая, когда первым символом в определении класса является диакритический знак, в таком случае символ темы не должен входить в набор, определенный классом. Если необходимо использовать диакритический знак в качестве члена класса, убедитесь, что он не является первым символом, или экранируйте его обратной косой чертой.

Например, класс символов [aeiou] соответствует любой прописной гласной, тогда как [^aeiou] соответствует любому символу, который не является прописной гласной.

Обратите внимание, что диакритический знак всего лишь служит для указания тех символов, которые не входят в класс, тем самым упрощая обозначение тех символов, которые в него входят. Класс, начинающийся с диакритического знака, не является утверждением: ему все равно требуется один символ из текста, и если текущий указатель поиска находится в конце строки, то сопоставления шаблону не происходит.

При сопоставлении без учета регистра все буквы в классе представляют свои как заглавные, так и строчные версии.

Символ минуса (дефис) можно использовать для обозначения диапазона символов в классе символов. Например, [d-m] соответствует любой букве от d до m включительно. Если в классе требуется символ минуса, он должен быть предварен обратной косой чертой или стоять в позиции, где он не может быть интерпретирован как обозначение диапазона (как правило, первым или последним в классе).

Буквенный символ "]" не может быть конечным символом диапазона. Шаблон вида [W-]46] будет интерпретирован как класс, состоящий из двух символов ("W" и "-"), за которым следует буквенная строка "46]", и, таким образом, будет соответствовать "W46]" или "46]". Однако, если "]" предварить символом обратной косой черты, он будет интерпретирован как конец диапазона, таким образом, [W-\]46] будет интерпретирован как класс, содержащий диапазон, за которым следуют два символа. Восьмеричные или десятеричные представления "]" также могут использоваться в качестве конца диапазона.

Типы символов \d, \D, \p, \P, \s, \S, \w и \W могут также появляться в классе символов, добавляя в класс символы, которым они соответствуют.

Единственные метасимволы, которые распознаются в классах символов, это обратная косая черта, дефис (только там, где его можно интерпретировать как определитель диапазона), диакритический знак (только в начале), открывающая квадратная скобка (только там, где ее можно интерпретировать как ввод имени класса POSIX, - см. следующий раздел) и закрывающая квадратная скобка. Однако экранирование обратной косой чертой любых других неалфавитно-цифровых символов также не повредит.

Классы символов POSIX

PCRE поддерживает условные обозначения POSIX для классов символов. Например,

[01[:alpha:]%]

соответствует "0", "1", любому алфавитному символу или "%". Поддерживаемые имена классов

alnum буквы и цифры,

alpha буквы,

ascii коды символов 0 – 127,

blank только пробел или знак табуляции,

cntrl управляющие символы,

digit десятичные цифры (тоже самое, что и \d),

graph печатные символы, кроме пробела,

lower строчные буквы,

print печатные символы, включая пробел,

punct печатные символы, кроме букв и цифр,

space символ пробела (не совсем тоже самое, что и \s),

upper заглавные буквы,

word   алфавитно-цифровые символы (тоже, что и \w),

xdigit десятеричные цифры.

Вертикальная черта (|)

Символ вертикальной черты используется для разделения альтернативных шаблонов. Например, шаблон

gilbert|sullivan

соответствует либо "gilbert", либо "sullivan". Допускается любое количество вариантов, в том числе и пустой (соответствующий пустой строке). В процессе подбора соответствия каждый вариант проверяется по очереди, слева направо, и используется первый найденный. Если варианты находятся внутри подшаблона (см. ниже), "найденный" означает соответствие как остальной части основного шаблона, так и варианту в подшаблоне.

Установка внутренних опций

Установки опций PCRE_CASELESS, PCRE_MULTILINE и PCRE_EXTENDED можно локально изменить в шаблоне с помощью последовательности букв-опций Perl, заключенных между "(?" и ")".

Буквы-опции:

i для PCRE_CASELESS

m для PCRE_MULTILINE

x для PCRE_EXTENDED

Например, (?im) определяет многострочный поиск совпадения без учета регистра. Также возможно отключить эти опции, предварив их дефисом, можно комбинировать установку и отмену опций, например, (?im-x) устанавливает PCRE_CASELESS и PCRE_MULTILINE и отменяет PCRE_EXTENDED. Если перед дефисом или после него появляется буква, опция отменяется.

Подшаблоны

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

1.Локализирует набор вариантов. Например, шаблон

cat(aract|erpillar|)

соответствует одному из слов: "cat", "cataract" или "caterpillar". Без скобок он бы соответствовал "cataract", "erpillar" или пустой строке.

2.Указывает на необходимость захвата подстроки. Открывающие скобки нумеруются слева направо (начиная с 1) и их порядковые номера используются для нумерации соответствующих подстрок в результате.

Например, если строка "the red king" сопоставляется с шаблоном

the ((red|white) (king|queen))

будут захвачены подстроки "red king", "red" и "king" и пронумерованы соответственно 1, 2 и 3.

То, что простые круглые скобки выполняют две функции, не всегда удобно. Бывают случаи, когда необходима группировка вариантов без захвата строки. В случае, когда после открывающей круглой скобки следует "?:", захват строки не происходит и текущий подшаблон не нумеруется. Например, если строка "the white queen" сопоставляется с шаблоном

((?:red|white) (king|queen))

будут захвачены подстроки "white queen" и "queen" и пронумерованы 1 и 2. Максимальное количество захватывающих подшаблонов 65535, а максимальная глубина вложенности всех подшаблонов, как захватывающих, так и незахватывающих, 200.

В случае, если в незахватывающем подшаблоне необходимо указать дополнительные опции, можно воспользоваться удобным сокращением: поместить символ, обозначающий устанавливаемую опцию, между "?" и ":". Таким образом, следующие два шаблона

(?i:saturday|sunday)

(?:(?i)saturday|sunday)

соответствуют одному и тому же набору строк. Так как альтернативные ветви проверяются слева направо, и установленные опции сохраняют свое действие до конца подшаблона, опция, установленная в одной ветке, также действует во всех последующих ветках. Таким образом, вышеприведенные шаблоны соответствуют как "SUNDAY", так и "Saturday".

Повторение

Повторение задается квантификаторами, следующими за любым из указанных ниже элементов:

буквенный символ данных,

метасимвол . (точка),

управляющая последовательность \C,

управляющая последовательность, соответствующая одному символу, например, \d,

класс символов,

обратная ссылка (см. следующий раздел),

подшаблон, заключенный в круглые скобки (если он не является утверждением).

Обычный квантификатор повторения обозначает минимальное и максимальное число допустимых сопоставлений путем заключения в фигурные скобки двух чисел, разделенных запятой. Числа должны быть меньше 65536, а первое из них должно быть меньше или равным второму. Например:

z{2,4}

соответствует "zz", "zzz" или "zzzz". Закрывающая фигурная скобка сама по себе не является специальным символом. Если второе число опущено, но запятая присутствует, верхнего ограничения не существует. Если пропущены и второе число, и запятая, квантификатор определяет точное число требуемых соответствий, например,

[aeiou]{3,}

соответствует, по крайней мере, 3 (и более) следующим друг за другом гласным, в то время как

\d{8}

сопоставляется точно 8 цифрам. Открывающая фигурная скобка, появляющаяся в позиции, где квантификатор не допускается, или в позиции, не соответствующей синтаксису квантификатора, рассматривается как буквенный символ. Например, {,6} является не квантификатором, а буквенной строкой, состоящей из 4 символов.

Квантификатор {0} допускается. Причем выражение ведет себя так, будто предыдущий элемент и квантификатор не существуют.

Для удобства (и обратной совместимости) три наиболее распространенных квантификатора имеют односимвольные сокращения:

* эквивалентен {0,}

+ эквивалентен {1,}

? эквивалентен {0,1}

Можно конструировать бесконечные циклы, введя после подшаблона, который не совпадает ни с одним символом, квантификатор, не имеющий верхнего предела, например:

(a?)*

По умолчанию, квантификаторы являются "жадными", что означает, что они совпадают максимально возможное количество раз (до максимально допустимого количества раз), не вызывая неудачи выполнения остальной части шаблона. Классическим примером, когда это создает проблемы, является поиск соответствия в комментариях в программах C. Комментарии могут находиться между символами /* и */, а внутри комментария могут появляться отдельные символы * и /. Попытка найти совпадение с комментариями C путем применения шаблона

/\*.*\*/

к строке

/* первый комментария */ не комментарий

/* второй комментарий */

потерпит неудачу, потому что происходит совпадение со всей строкой из-за жадности элемента *.

Однако, если за квантификатором следует знак вопроса, он перестает быть жадным и совпадает минимально возможное количество раз. Тогда шаблон

/\*.*?\*/

правильно обрабатывает комментарий С. Значение других квантификаторов при этом не изменяется, они обеспечивают необходимое число совпадений. Не следует путать это употребление знака вопроса с использованием его в качестве квантификатора как такового. Поскольку он может использоваться двояко, иногда его можно дублировать, как в

\d??\d

что в первую очередь соответствует одной цифре, но может соответствовать и двум, если это единственный способ совпадения с оставшейся частью шаблона.

Если установлена опция PCRE_UNGREEDY, квантификатор утрачивает жадность по умолчанию, отдельные квантификаторы могут наделяться жадностью, если за ними следует знак вопроса. Другими словами, им возвращается их поведение по умолчанию.

Если подшаблон в круглых скобках квантифицируется с минимальным числом повторений, которое больше 1, или имеет ограничение максимума, для скомпилированного шаблона потребуется больше памяти, пропорционально размеру минимума или максимума.

Поэлементное группирование и притяжательные квантификаторы

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

Рассмотрим для примера шаблон \d+foo применительно к тексту:

123456bar

После совпадения всех 6 цифр и констатации отсутствия совпадения с "foo", поисковой механизм сделает еще одну попытку найти совпадение для элемента \d+, но уже из 5 цифр, после очередной неудачи будет сопоставлено 4 цифры и так далее.

С помощью «поэлементного группирования» (atomic grouping, термин взят из книги Jeffrey Friedl) указывается, что если одна часть шаблона была сопоставлена, ее не следует анализировать повторно.

Если мы воспользуемся группированием на предыдущем примере, поисковый механизм немедленно прекратит поиск, констатировав несовпадение с "foo" с первого раза. Записываются однократные шаблоны при помощи круглых скобок следующим образом: (?>. Например:

(?>\d+)foo

Такой вид скобок "блокирует" содержащуюся в них часть шаблона при первом же обнаружении совпадения, а при обнаружении несоответствия с остальной частью шаблона предотвращает повторный анализ заблокированной части. Однако это не мешает повторно анализировать любые другие элементы, в том числе предшествующие группе.

Другими словами, можно сказать, что подшаблон такого типа соответствует строке символов, которой бы соответствовал идентичный отдельный шаблон, если бы он был привязан к («заякорен на») текущей позиции текста.

Подшаблоны поэлементного группирования не являются захватывающими. Простые примеры, подобные приведенному выше, можно охарактеризовать как безусловный захват максимального количества повторений. Таким образом, в то время как \d+ и \d+? применяются для регулирования количества цифр, которые должны совпасть, чтобы совпала остальная часть шаблона, (?>\d+) может совпадать только со всей последовательностью цифр.

Поэлементные группы могут, конечно, содержать произвольные сложные подшаблоны и могут быть вложенными. Однако, когда подшаблон для группирования является единичным повторяемым элементом, как в примере выше, можно использовать более простое обозначение, которое называется «притяжательным квантификатором». Притяжательный квантификатор указывается с помощью дополнительного символа плюс + после квантификатора. Используя это обозначение, предыдущий пример можно переписать как

\d++foo

Притяжательные квантификаторы всегда жадные; установка опции PCRE_UNGREEDY игнорируется. Они являются удобным обозначением для простых форм поэлементного группирования. Однако разницы в значении или обработке притяжательного квантификатора и соответствующей поэлементной группы нет.

Если шаблон содержит неограниченное повторение внутри подшаблона, который сам по себе может быть повторен неограниченное число раз, использование поэлементного группирования является единственным способом избежать ложных совпадений, которые занимают много времени. Шаблон

(\D+|<\d+>)*[!?]

соответствует неограниченному количеству подстрок, которые либо состоят из не цифр, либо цифры в них заключены в угловые скобки <>, за которыми следует либо восклицательный знак !, либо вопросительный ?. При обнаружении совпадения процесс происходит быстро. Однако применительно к

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

уйдет много времени, пока будет сделано заключение о несоответствии. Происходит это в силу того, что строка может быть разделена многими различными способами между внутренним повторением \D+ и внешним повторением *, и они все должны быть перебраны. (В примере использовано [!?], что предпочтительнее, чем единичный символ в конце, потому что в PCRE есть оптимизация, которая допускает быстрое обнаружение несовпадения при использовании единичного символа. Запоминается последний единичный символ, необходимый в соответствии, и если символ не присутствует в строке, быстро возвращается результат с несовпадением.) При изменении шаблона с использованием поэлементного группирования, например:

((?>\D+)|<\d+>)*[!?]

последовательность нецифровых символов не может быть разорвана, и вывод о несоответствии делается быстро.

Обратные ссылки

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

Обратная косая черта всегда рассматривается как обратная ссылка, если десятичное число после нее меньше 10, и приводит к ошибке, если нет соответствующего числа открывающих скобок. Другими словами, для чисел меньше 10 скобки, на которые идет ссылка, необязательно должны быть слева от ссылки. Обработка цифр после обратной косой черты подробно рассмотрена выше в подразделе "Неотображаемые символы".

Обратная ссылка сопоставляется с частью строки, захваченной соответствующим подшаблоном, но не с самим подшаблоном.

Таким образом, шаблон

(sens|respons)e and \1ibility

совпадает с "sense and sensibility" и "response and responsibility", но не с "sense and responsibility". В случае если обратная ссылка обнаружена во время регистрозависимого поиска, то при сопоставлении обратной ссылки регистр также учитывается. Например,

((?i)rah)\s+\1

совпадает с "rah rah" и "RAH RAH", но не "RAH rah", хотя основной захватывающий подшаблон сопоставляется без учета регистра.

Обратные ссылки к названным подшаблонам используют синтаксис Python (?P=name). Пример, приведенный выше, можно переписать следующим образом:

(?(?i)rah)\s+(?P=p1)

К одному и тому же подшаблону может быть больше одной обратной ссылки. Если подшаблон фактически не использован в каком-либо сопоставлении, любая обратная ссылка к нему всегда приводит к неудаче. Например, шаблон

(a|(bc))\2

всегда терпит неудачу, если находит соответствие с "a" раньше, чем с "bc". Поскольку в шаблоне может присутствовать множество захватывающих скобок, все цифры, следующие за обратной косой чертой, рассматриваются как часть потенциальной обратной ссылки.

Если за ссылкой должна следовать цифра, необходимо использовать ограничитель обратной ссылки. Если установлена опция PCRE_EXTENDED, этим ограничителем может быть символ пробела. В противном случае можно использовать пустой комментарий.

Ссылка на подшаблон, внутри которого она расположена, всегда терпит неудачу, если это первое сопоставление текущего под­шаблона. Например, (a\1) не будет ничему соответствовать. Однако такие ссылки могут быть полезны внутри повторяющихся подшаблонов. Например, шаблон

(a|b\1)+

соответствует любому количеству "a" и "aba", "ababbaa" и т.д. При каждой итерации шаблона обратная ссылка соответствует той части строки, которая была захвачена при предыдущей итерации. Чтобы это работало, шаблон должен быть построен так, чтобы при первой итерации сопоставления с обратной ссылкой не проводилось. Этого можно достичь использованием чередования, как в предыдущем примере, или квантификатора с минимумом, равным нулю.

Утверждения

Утверждение  это проверка символов, идущих до или после текущей позиции сопоставления, которая не затрагивает каких-либо символов из строки темы. Простые утверждения \b, \B, \A, \G, \Z, \z, ^ и $ описаны выше.

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

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

Утверждения с просмотром вперед

Утверждения с просмотром вперед начинаются с (?= для положительных утверждений и с (?! для отрицательных утверждений. Например,

\w+(?=;)

соответствует слову, за которым идет точка с запятой, но не включает точку с запятой в соответствие, а

foo(?!bar)

соответствует любому найденному "foo", за которым не следует "bar". Следует учесть, что явно схожий шаблон

(?!foo)bar

не будет искать вхождение "bar", перед которым стоит не "foo", а просто будет искать любое "bar", так как утверждение (?!foo) всегда верно, если следующие три символа  "bar". Для достижения обратного эффекта необходимо утверждение с просмотром назад.

Если необходимо вызвать остановку поиска в какой-то точке шаблона, удобнее всего использовать (?!), потому что пустая строка всегда сопоставляется и, таким образом, утверждение, требующее, чтобы пустой строки не было, всегда терпит неудачу.

Утверждения c просмотром назад

Утверждения с просмотром назад начинаются с (?<= для положительных утверждений и с (?<! для отрицательных утверждений. Например,

(?<!foo)bar

не находит вхождение "bar", перед которым не следует "foo". Содержимое утверждений с просмотром назад ограничено таким образом, что все строки, которым они соответствуют, должны иметь фиксированную длину. Однако, если существует несколько вариантов, им необязательно быть одной и той же фиксированной длины, таким образом,

(?<=bullock|donkey)

допускается, но

(?<!dogs?|cats?)

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

(?<=ab(c|de))

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

(?<=abc|abde)

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

В PCRE не допускается экранирование последовательностью \C в утверждениях с просмотром назад, потому что невозможно высчитать длину просмотра назад. Экранирование последовательностью \X, которая может соответствовать различному количеству байтов, также не допускается.

Поэлементные группы могут использоваться совместно с утверждениями с просмотром назад для обозначения эффективных сопоставлений в конце строки темы. Рассмотрим простой шаблон

abcd$

в применении к длинной строке, не соответствующей указанной маске. Так как процесс поиска соответствия осуществляется слева направо, PCRE просмотрит каждое "a" в теме и только потом будет анализировать следующие записи в шаблоне. Если шаблон задан как

^.*abcd$

сначала .* сопоставляется со всей строкой, но когда это сопоставление терпит неудачу (так как далее не следует никакого "a"), производится сопоставление со всеми символами, кроме последнего, затем кроме последних двух и т.д. В конечном итоге поиск "a" захватывает всю строку, справа налево, что ничем не лучше предыдущего поиска. Однако, если шаблон записать в виде

^(?>.*)(?<=abcd)

или, что то же самое, с использованием синтаксиса притяжательного квантификатора

^.*+(?<=abcd)

повторное сопоставление для символа .* не выполняется; он может соответствовать только всей строке целиком. Последующее утверждение с просмотром назад только однократно проверяет последние четыре символа. Если проверка терпит неудачу, сразу же терпит неудачу и все сопоставление. При анализе длинных строк разница по времени значительна.

Использование многочисленных утверждений

Количество следующих друг за другом утверждений (любого порядка) не ограничено. Например,

(?<=\d{3})(?<!999)foo

соответствует "foo", перед которым находятся три цифры, но не "999". Заметьте, что каждое из утверждений применяется самостоятельно в одной и той же позиции в строке темы. Сначала проводится проверка того, являются ли три предыдущие символа цифрами, а затем проверяется, не являются ли эти три цифры "999". Этот шаблон не сопоставляется с "foo", перед которым расположены 6 символов, первые из которых являются цифрами, а последние три не являются "999". Например, он не сопоставляется с "123abc-foo". Для этого требуется следующий шаблон

(?<=\d{3}...)(?<!999)foo

Здесь первое утверждение просматривает предыдущие шесть символов, проверяя, являются ли первые три цифрами, а затем второе утверждение проверяет, не являются ли предшествующие три цифры "999".

Утверждения могут быть вложены в любой комбинации. Например,

(?<=(?<!foo)bar)baz

сопоставляет вхождение "baz", перед которым находится "bar", перед которым, в свою очередь, не стоит "foo", тогда как

(?<=\d{3}(?!999)...)foo

это другой шаблон, который соответствует "foo", перед которым находятся три цифры и любые три символа, не являющиеся "999".

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

Процесс поиска соответствия можно подчинить условию или выбрать из двух альтернативных подшаблонов в зависимости от результата утверждения или успеха сопоставления с предыдущим захватывающим подшаблоном. Существует две возможные формы условных шаблонов

(?(condition)yes-pattern)

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

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

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

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

Первая часть соответствует факультативной открывающей круглой скобке и, если этот символ присутствует, устанавливает его как первую захваченную подстроку. Вторая часть соответствует одному или нескольким символам, не являющимся круглыми скобками. Третья часть является условным подшаблоном, который проверяет, был ли сопоставлен первый набор скобок или нет. В случае обнаружения совпадения, т.е., если текст начинался открывающей скобкой, условие будет интерпретировано как истинное, а следовательно, применяется yes-шаблон и требуется закрывающая скобка. В противном случае, так как нет no-шаблона, шаблон ничему не сопоставляется. Другими словами, этот шаблон совпадает с последовательностью не-скобок, факультативно заключенной в скобки.

Если условием является строка (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,  где aaa  буквы и dd  цифры.