Учебник РНР
Назад Вперёд

Синтаксис Патэрна

(unknown)

Синтаксис патэрна - Описывает синтаксис PCRE regex.

Описание

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

Отличия от Perl

Эти отличия даны относительно Perl 5.005.

  1. По умолчанию пробельным символом считается любой таковой символ, распознаваемый функцией isspace() библиотеки С, хотя возможна компиляция PCRE с альтернативной таблицей типов символов. Нормально isspace() распознаёт space, formfeed, newline, carriage return, horizontal tab и vertical tab. Perl 5 больше не включает vertical tab в набор пробельных символов. Мнемоника \v, которая долгое время была в документации Perl, фактически никогда не распознавалась. Однако сам по себе этот символ рассматривался как пробельный как минимум до версии 5.002. В 5.004 и 5.005 он не соответствует \s.

  2. PCRE не разрешает повторение квантификаторов или опережающих утверждений/lookahead assertions. Perl разрешает их, но они имеют другое значение. Например, (?!a){3} не утверждает, что три последующие символа не "a". Оно просто утверждает три раза, что следующий символ не "a".

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

  4. Хотя двоичные нулевые символы поддерживаются в строке-субъекте, они не допускаются в строке патэрна, поскольку он передаётся как нормальная C-строка, оканчивающаяся нулём. Замена "\\x00" может использоваться в патэрне для представления бинарного нуля.

  5. Следующие escape-последовательности (мнемоники) Perl не поддерживаются: \l, \u, \L, \U, \E, \Q.
    Фактически они реализованы в общей обработке строк в Perl и не являются частью его машины патэрнов.

  6. Perl-утверждение \G не поддерживается, так как оно не относится к одиночным совпадениям патэрна.

  7. Вполне очевидно, что PCRE не поддерживает конструкцию (?{code}).

  8. На момент написания в Perl 5.005_02 имелись некоторые странности, касающиеся установок захваченных строк, если часть патэрна повторяется. Например, сравнение "aba" с патэрном /^(a(b)?)+$/ устанавливает в $2 значение "b", но сравнение "aabbaa" с /^(aa(bb)?)+$/ оставляет $2 не установленной. Однако, если патэрн изменить на /^(aa(b(b))?)+$/ , то $2 (и $3) устанавливаются. В Perl 5.004 переменная $2 устанавливается в обоих случаях и является TRUE в PCRE. Если в будущем Perl будет изменён для приведения этого в соответствие, PCRE может также измениться.

  9. Другое пока не разрешённое несоответствие - то, что в Perl 5.005_02 патэрн /^(a)?(?(1)a|b)+$/ совпадает со строкой "a", а в PCRE - нет. Однако и в Perl, и в PCRE патэрн /^(a)?a/ совпадает с "a" и оставляет $1 не установленной.

  10. PCRE предоставляет некоторые расширения к регулярным выражениям Perl:

    1. Хотя смотрящие назад/lookbehind утверждения обязаны совпадать со строками фиксированной длины, каждая альтернативная ветвь lookbehind-утверждения может со строкой другой длины. Perl 5.005 требует, чтобы все они имели одну длину.

    2. Если PCRE_DOLLAR_ENDONLY установлено, а PCRE_MULTILINE - нет, метасимвол $ совпадает только с самым концом строки.

    3. Если PCRE_EXTRA установлено, то обратный слэш с буквой, не имеющей специального значения, является ошибочным.

    4. Если PCRE_UNGREEDY установлено, то жадность квантификаторов повторения инвертируется, то есть, по умолчанию они будут нежадными, но если за ними следует знак вопроса, будут жадными.

Регулярные выражения. Детали.

Введение

Ниже описаны синтаксис и семантика регулярных выражений, поддерживаемых в PCRE. Регулярные выражения описаны также в документации Perl и в некоторых других книгах, некоторые из которых содержат копии примеров. Книга Jeffrey Friedl'а "Mastering Regular Expressions", опубликованная O'Reilly (ISBN 1-56592-257-3), рассматривает их в деталях. Данное описание может служить справочником.
Регулярное выражение это патэрн, который сравнивается со строкой-субъектом слева направо. Большинство символов в патэрне представляют сами себя и совпадают с соответствующими символами в строке-субъекте. В качестве тривиального примера - патэрн The quick brown fox совпадает с частью строки-субъекта, которая полностью идентична ему.

Метасимволы

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

Имеются два различных набора метасимволов: распознаваемые в любом месте патэрна, кроме квадратных скобок, и те, которые распознаются в квадратных скобках.

Вот символы за пределами квадратных скобок:

\

общий escape-символ для различных вариантов использования

^

начало утверждения субъекта (или строки, в многострочном режиме)

$

конец утверждения субъекта (или строки, в многострочном режиме)

.

совпадает с любым символом, кроме newline (по умолчанию)

[

начало определения класса символов

]

конец определения класса символов

|

начало альтернативной ветви

(

начало субпатэрна

)

конец субпатэрна

?

расширяет значение (, также квантификатор 0 или 1, также минимизатор квантификатора

*

квантификатор 0 или более

+

квантификатор1 или более

{

начало min/max квантификатора

}

конец min/max квантификатора

Часть патэрна в квадратных скобках называется "классом символов".
В классе символов метасимволами являются только:
\

общий escape-символ

^

отрицает этот класс, но только если является первым символом

-

обозначает диапазон символов

]

конец класса символов

В следующих разделах рассматривается использование каждого из этих метасимволов.

backslash/обратный слэш

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

Например, если вы хотите найти совпадение с символом "*", вы записываете в патэрне "\*". Это будет работать независимо от того, может ли последующий символ интерпретироваться как метасимвол, поэтому всегда надёжнее записывать неалфавитный символ с "\", чтобы специфицировать, что он представляет сам себя. Особенно если вы хотите найти совпадение с backslash - тогда вы записываете "\\".

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

Во-вторых, backslash предоставляет способ кодирования в патэрне непечатаемых символов видимым образом. Ограничений на появление непечатаемых символов нет, за исключением двоичного нуля, который оканчивает патэрн, но если патэрн подготавливается путём редактирования текста, то обычно легче использовать одну из следующих escape-последовательностей (замен), а не бинарный символ, представляемых ими:

\a

alarm/предупреждение, то есть символ BEL (hex 07)

\cx

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

\e

escape (hex 1B)

\f

formfeed/прогон страницы (hex 0C)

\n

newline/новая строка (hex 0A)

\r

carriage return/возврат каретки (hex 0D)

\t

tab/табуляция (hex 09)

\xhh

символ с 16-ричным кодом hh

\ddd

символ с 8-ричным кодом ddd, или backreference/обратная ссылка

Эффект от применения "\cx" таков: если "x" это символ в нижнем регистре, он конвертируется в верхний регистр. Затем бит 6 символа (hex 40) инвертируется. Таким образом, "\cz" становится hex 1A, "\c{" становится hex 3B, а "\c;" становиться hex 7B.

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

После "\0" читаются не более четырёх 8-ричных цифр. В обоих случаях, если имеется менее двух цифр, используются именно те, которые представлены. Таким образом, последовательность "\0\x\07" специфицирует два бинарных нуля с последующим символом BEL. Убедитесь, что вы предоставили две цифры после начального нуля, если последующий символ сам является 8-ричным числом.

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

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

\040

другой способ записи space

\40

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

\7

это всегда back reference/обратная ссылка

\11

может быть back reference или другой способ записи tab

\011

это всегда tab

\0113

это tab с последующим символом "3"

\113

это символ с 8-ричным кодом 113 (поскольку не может быть более 99 обратных ссылок)

\377

это байт, состоящий из битовых 1

\81

это back reference или бинарный нуль с последующими двумя символами "8" и "1"

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

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

Третий вариант использования backslash - специфицирование общего типа символов:

\d

любое 10-ричное число

\D

любой символ - не 10-ричное число

\s

любой пробельный символ

\S

любой непробельный символ

\w

любой "word/словарный" символ

\W

любой "non-word/несловарный" символ

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

Символ "word" это любая буква, или цифра, или символ подчёркивания, то есть любой символ, который может быть частью "word" в Perl. Определение букв и цифр контролируется таблицами символов PCRE и может варьироваться, если имеет место подстановка с локальной спецификой (см. ранее "Поддержка локализации"). Например, при локализации "fr" (French) используются некоторые символы с кодами выше 128 для ввода букв с акцентами, и они совпадают с \w.

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

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

\b

граница слова

\B

не граница слова

\A

начало субъекта (не зависит от многострочного режима)

\Z

конец субъекта или newline в конце (не зависит от многострочного режима)

\z

конец субъекта (не зависит от многострочного режима)

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

Граница слова это такая позиция в строке-субъекте, где текущий и предыдущий символы не совпадают с \w или \W (т.е. один совпадает с \w, а другой - с \W), или начало или конец строки, если первый или последний символ совпадает с \w, соответственно.

Утверждения \A, \Z и \z отличаются от традиционных circumflex и dollar (описано далее) тем, что они совпадают только с самым началом и концом строки-субъекта, независимо от установленных опций. На них не влияют опции PCRE_NOTBOL или PCRE_NOTEOL. Разница между \Z и \z в том, что \Z совпадает до newline, то есть является последним символом строки, а также концом строки, в то время как \z совпадает только в конце строки.

Circumflex и dollar

Вне класса символов, в режиме подстановки по умолчанию, символ circumflex (^) является утверждением, которое true, только если текущая точка совпадения является началом строки-субъекта. Внутри класса символов circumflex имеет совершенно иное значение (см. далее).

Circumflex не должен быть первым символом патэрна, если используются несколько альтернатив, но должен быть первым в каждой альтернативе, в которой появляется, если патэрн совпадает с данной ветвью. Если все имеющиеся альтернативы начинаются с circumflex (^), то есть если патэрн ограничен для совпадения только в начале субъекта, говорится, что это "заякоренный/anchored" патэрн. (Имеются также и другие конструкции, которые могут вызывать заякоривание патэрна.)

Символ dollar является утверждением, которое TRUE, только если текущая точка совпадения находится в конце строки-субъекта или сразу после символа newline, который является последним символом строки (по умолчанию). Dollar не должен быть последним символом патэрна, если дано несколько альтернатив, но должен быть последним символом в любой ветви, в которой он появляется. Dollar не имеет специального значения в классе символов.

Значение dollar может быть изменено так, чтобы он совпадал только с самым концом строки, через установку опции PCRE_DOLLAR_ENDONLY во время компиляции или подстановки. Это не влияет на утверждение \Z.

Значение символов circumflex и dollar изменяется, если установлена опция PCRE_MULTILINE. В этом случае они совпадают сразу после или сразу до внутреннего символа "\n", соответственно, в дополнение к совпадению в начале и в конце строки-субъекта. Например, патэрн /^abc$/ совпадает со строкой-субъектом "def\nabc" в многострочном режиме, но не иначе. Соответственно, патэрны, которые заякорены в однострочном режиме, если все ветви начинаются с "^", не являются заякоренными в многострочном режиме. Опция PCRE_DOLLAR_ENDONLY игнорируется, если установлена PCRE_MULTILINE.

Заметьте, что последовательности \A, \Z и \z могут использоваться для совпадения с началом и концом субъекта в обоих режимах, и если все верви в начале патэрна с \A всегда заякорены, независимо от того, установлена PCRE_MULTILINE или нет.

FULL STOP/ПОЛНЫЙ ОСТАНОВ

Вне класса символов точка в патэрне совпадает с одним из символов субъекта, включая непечатаемый символ, но не с символом (по умолчанию) newline. Если установлена опция PCRE_DOTALL, точки совпадают также и с символами newline. Обработка точки зависит всецело от обработки circumflex и dollar, их роднит только то, что они оба вводят символы newline. Точка не имеет специального значения в классе символов.

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

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

Класс символов совпадает с одиночным символом строки-субъекта; символ обязан быть из набора символов, определённого в данном классе, если только первым символом в классе не является circumflex - в этом случае символ строки-субъекта обязан не входить в набор, определённый в данном классе. Если circumflex необходим как член класса, убедитесь, что он не является первым символом, либо escape-ируйте его с помощью обратного слэша (\).

Например, класс символов [aeiou] совпадает с любой гласной буквой в нижнем регистре, а [^aeiou] совпадает с любым символом, не являющимся гласной буквой в нижнем регистре.
Обратите внимание, что circumflex это просто удобный символ для обозначения символов, не используемых для совпадения. Он не является утверждением: он просто использует символы из строки-субъекта и терпит неудачу, если указатель находится в конце строки.

Если установлено совпадение без учёта регистра, то любая буква класса представляет символы как нижнего, так и верхнего регистров, поэтому, например, безрегистровый [aeiou] совпадает с "A" и с "a", в безрегистровый [^aeiou] совпадает с не-"A" и с не-"a", в то время как регистровая версия совпадает.

Символ newline никогда не рассматривается специально в классах символов, вне зависимости от того, установлены опции PCRE_DOTALL или PCRE_MULTILINE или нет. Класс, такой как [^a], всегда будет совпадать с newline.

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

Невозможно иметь литеральный символ "]" в качестве конца диапазона. Патэрн [W- 46] интерпретируется как класс из двух символов ("W" и "-") с последующей литеральной строкой "46]", поэтому он совпадает с "W46]" или с "-46]". Однако, если "]" escape-ирован с обратным слэшем, он интерпретируется как конец диапазона, поэтому [W-\]46] интерпретируется как единый класс, содержащий диапазон, с последующими двумя отдельными символами. 8-ричное или 16-ричное представление "]" также может использоваться для указания на конец диапазона.

Диапазоны оперируют в ASCII-кодировке символов. Они также могут использоваться для символов, специфицированных числами, например [\000-\037]. Если диапазон букв установлен при отключённом распознавании регистра символов, он совпадает с буквами в любом регистре. Например, [W-c] эквивалентно [][\^_`wxyzabc], совпадая без учёта регистра и при использовании таблиц символов для локализации "fr", [\xc8-\xcb] совпадает с акцентированными символами E в любом регистре.

Типы символов \d, \D, \s, \S, \w и \W также могут появляться в классах символов и добавлять в класс символы, с которыми они совпадают. Например, [\dABCDEF] совпадает с любым 16-ричным числом. Символ circumflex (^) может быть использован с типами символов верхнего регистра, чтобы специфицировать более ограниченный набор символов - только типы нижнего регистра. Например, класс [^\W_] совпадает с любой буквой или цифрой, но не с символом подчёркивания.

Все неалфавитные символы, кроме \, -, ^ (в начале) и заключительного ], являются неспециальными в классах символов, но не повредит вводить их с помощью escape-последовательностей.

Vertical bar/Вертикальная черта

Символы вертикальной черты (|) используются для разделения альтернативных патэрнов.
Например, патэрн

gilbert|sullivan

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

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

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

i для PCRE_CASELESS

m для PCRE_MULTILINE

s для PCRE_DOTALL

x для PCRE_EXTENDED

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

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

(?i)abc
a(?i)bc
ab(?i)c
abc(?i)

что, в свою очередь, то же самое, что компиляция патэрна abc с установкой PCRE_CASELESS. Иначе говоря, такие установки "верхнего уровня" применяются ко всему патэрну (если отсутствуют другие изменения в субпатэрнах). Если имеются несколько установок одной опции на верхнем уровне, используется самая правая установка.

Если изменение опции возникает внутри субпатэрна, эффект может быть разным. Это изменение поведения было сделано в Perl 5.005.
Изменение опции в субпатэрне влияет только на ту часть субпатэрна, которая следует за ним; так

(a(?i)b)c

совпадает с abc и с aBc и больше ни с чем (предполагая, что PCRE_CASELESS не используется). Это означает, что опции могут изменяться для получения различных установок в разных частях патэрна. Любые изменения, сделанные в одной альтернативе, влияют на последующие ветви внутри того же субпатэрна. Например,

(a(?i)b|c)

совпадает с "ab", "aB", "c" и "C", хотя при совпадении с "C" первая ветвь покидается до установки опции. Это происходит из-за того, что установки опций происходят на этапе компиляции. Иначе поведение будет непредсказуемым.

PCRE-специфичные опции PCRE_UNGREEDY и PCRE_EXTRA могут быть изменены так же, как и Perl-совместимые опции, путём использования символов U и X соответственно. Установка флага (?X) является специальной с том плане, что он обязан появляться в патэрне раньше, чем будет включена любая дополнительная возможность, даже если она на верхнем уровне. Лучше всего помещать его при старте.

Субпатэрны

Субпатэрны ограничены скобками (круглыми), которые могут вкладываться. Маркировка части патэрна как субпатэрна выполняет два действия:

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

cat(aract|erpillar|)

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

2. Устанавливает субпатэрн как захватывающий субпатэрн (как определено выше). Когда совпадает весь патэрн целиком, часть строки-субъекта, совпавшая с субпатэрном, передаётся обратно вызывающему посредством аргумента ovector функции pcre_exec(). Открывающие скобки вычисляются слева направо (начиная с 1) для получения количества захватывающих субпатэрнов.

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

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

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

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

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

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

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

(?i:saturday|sunday)
(?:(?i)saturday|sunday)

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

Повторение

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

одиночного символа, возможно, мнемонизированного
метасимвола .
класса символов
обратной ссылки/back reference (см. следующий раздел)
субпатэрна в скобках (если это не утверждение/assertion, см. далее)

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

z{2,4}

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

[aeiou]{3,}

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

\d{8}

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

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

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

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

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

(a?)*

Ранние версии Perl и PCRE являются источниками ошибок в процессе компиляции таких патэрнов. Однако, поскольку бывают случаи, когда это необходимо, такие патэрны принимаются, но если любое повторение такого субпатэрна фактически не совпадает ни с какими символами, цикл форсированно прерывается.

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

/\*.*\*/

к строке

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

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

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

/\*.*?\*/

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

\d??\d

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

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

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

Если патэрн начинается с .* или с .{0,} и установлена опция PCRE_DOTALL (эквивалентная Perl'овской /s), разрешая, таким образом совпадение . с символами новой строки, то патэрн неявно заякоривается, поскольку всё, что идёт следом, будет испытываться относительно каждой символьной позиции в строке-субъекте, поэтому после первой нет другой позиции для возобновления попыток найти полное совпадение.
PCRE рассматривает такой патэрн так, как если бы ему предшествовало \A. Когда известно, что строка-субъект не содержит символов новой строки, предпочтительнее установить PCRE_DOTALL, если патэрн начинается с  .*, чтобы получить эту оптимизацию, или, альтернативно, использовать ^ для явного обозначения заякоривания.

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

(tweedle[dume]{3}\s*)+

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

/(a|(b))+/

совпадёт с "aba", значением второй захваченной подстроки будет "b".

Обратные Ссылки/BACK REFERENCES

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

Однако, если 10-ричное число, идущее после backslash, меньше 10, оно всегда считается обратной ссылкой и вызывает ошибку только тогда, когда во всём патэрне нет достаточного количества захватывающих левых скобок. Другими словами, скобки, на которые ссылаются, не обязаны быть слева от ссылки для числе менее 10. См. в разделе "Обратный слэш/Backslash" ранее детальную информацию об обработке чисел, идущих после обратного слэша.

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

(sens|respons)e and \1ibility

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

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

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

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

(a|(bc))\2

всегда терпит неудачу, если начинает совпадать с "a" раньше, чем с "bc". Поскольку может быть до 99 обратных ссылок, все цифры, идущие после backslash, считаются частью числа потенциальной обратной ссылки. Если патэрн продолжается цифровым символом, то обязан использоваться некоторый ограничитель для окончания обратной ссылки. Если опция PCRE_EXTENDED установлена, это может быть пробел. Иначе может использоваться пустой комментарий.
Обратная ссылка, которая появляется внутри скобок, к которым она обращается, терпит неудачу, если сначала используется субпатэрн; так, например, (a\1) никогда не совпадает. Однако такие ссылки могут использоваться внутри повторяющихся субпатэрнов. Например, патэрн

(a|b\1)+

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

Утверждения/Assertions

Утверждение это проверка символов, идущих следом или предшествующих текущей точке совпадения, не используя реально никаких символов. Простые утверждения, кодированные как \b, \B, \A, \Z, \z, ^ и $, были рассмотрены ранее. Более сложные утверждения кодированы как субпатэрны.
Есть утверждения двух видов: те, которые смотрят вперёд/ahead от текущей позиции, в строке-субъекте, и те, которые смотрят назад/behind.

Субпатэрн утверждения совпадает обычным образом, за исключением того, что он не вызывает изменения текущей позиции совпадения. Утверждения вперёд начинаются с (?= для положительного утверждения и с (?! - для отрицательного утверждения. Например,

\w+(?=;)

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

foo(?!bar)

совпадает с любым появлением "foo", после которого не идёт "bar". Обратите внимание, что похожий патэрн

(?!foo)bar

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

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

(?<!foo)bar

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

(?<=bullock|donkey)

допустимо, а

(?<!dogs?|cats?)

вызовет ошибку времени компиляции. Ответвления, которые совпадают со строками разной длины, допускаются только на верхнем уровне lookbehind-утверждения. Это расширение относительно 5.005, который требует, чтобы все ответвления совпадали с одной длиной строки. Такое утверждение как

(?<=ab(c|de))

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

(?<=abc|abde)

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

Разные утверждения (любого вида) могут следовать друг за другом. Например,

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

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

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

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

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

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

совпадает с "baz" с предшествующим "bar", перед которым, в свою очередь, нет "foo", а

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

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

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

Утверждения обсчитываю максимум до 200 субпатэрнов.

Once-only/"Однократные" субпатэрны

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

Рассмотрим, например, патэрн \d+foo в применении к строке-субъекту

123456bar

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

(?>\d+)bar

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

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

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

Эта конструкция, разумеется, может содержать произвольно усложнённые субпатэрны и может вкладываться.

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

abcd$

применённый к длинной строке, с которой он не совпадает. Поскольку совпадение выполняется слева направо, PCRE будет искать каждую "a" в субъекте, а затем посмотрит, что из последующего совпадает с остатком патэрна. Если патэрн специфицирован так:

^.*abcd$

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

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

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

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

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

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

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

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

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

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

Условные субпатэрны

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

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

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

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

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

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

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

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

Условие это положительное опережающее/lookahead утверждение, которое совпадает с необязательной последовательностью не-букв с последующей буквой. Другими словами, оно проверяет наличие минимум одной буквы в субъекте. Если буква найдена, вновь проверяется совпадение субъекта с первой альтернативой; иначе проверяется совпадение со второй альтернативой. Этот патэрн совпадает со строками одной из двух форм: dd-aaa-dd или dd-dd-dd, где aaa это буквы, а dd это цифры.

Комментарии

Последовательность символов (?# обозначает начало комментария, который продолжается до следующей закрывающей скобки. Вложение скобок не допускается. Символы, образующие комментарий, не являются частью совпадения патэрна.

Если установлена опция PCRE_EXTENDED, не escape-ированный символ # вне класса символов начинает комментарий, который продолжается до следующего символа новой строки в патэрне.

Рекурсивные патэрны

Рассмотрим проблему совпадения строки в скобках, когда допускается неограниченное вложение скобок. Без использования рекурсии лучшее, что можно сделать, это использовать патэрн, который совпадает на некоторую фиксированную глубину вложения. Невозможно обработать вложения на произвольно большую глубину. В Perl 5.6 имеется экспериментальная возможность, позволяющая (помимо прочего) выполнять рекурсию регулярных выражений. Специальный элемент (?R) предоставлен для этого специфического case/варианта рекурсии.
PCRE-патэрн разрешает проблему скобок (предполагая, что опция PCRE_EXTENDED установлена так, что пробел игнорируется):

\( ( (?>[^()]+) | (?R) )* \)

Вначале он совпадает с открывающей скобкой. Затем - с любым количеством подстрок, которые могут быть либо последовательностями не-скобок, либо рекурсивным совпадением самого патэрна (т.е. корректно заключённой в скобки подстрокой). Наконец, идёт закрывающая скобка.

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

(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa()

то он быстро даст "нет совпадений". Однако, если однократный/once-only субпатэрн не используется, поиск совпадения будет продолжаться длительное время, так как есть много различных способов, которыми повторения + и * могут кроить строку-субъект, и все они должны быть проверены, прежде чем будет выдано сообщение о неудаче поиска.

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

(ab(cd)ef)

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

\( ( ( (?>[^()]+) | (?R) )* )\)

то строка, захватываемая ими, будет "ab(cd)ef" - содержимое скобок верхнего уровня.
Если в патэрне имеются более 15 пар захватывающих скобок, PCRE должен получать дополнительную память для хранения данных в процессе рекурсии, которую он выделяет через использование pcre_malloc, освобождая память затем через pcre_free. Если невозможно выделить память, он сохраняет данные только для первых 15 захватывающих скобок, так как нет способа выдать ошибку out-of-memory изнутри рекурсии.

Производительность

Некоторые элементы, которые могут появляться в патэрнах, работают более эффективно, чем другие. Более эффективно использовать класс символов, такой как [aeiou], нежели набор альтернатив, такой как (a|e|i|o|u). В целом, более простая конструкция является более эффективной. Книга Jeffrey Friedl'а содержит большую дискуссию об оптимизации регулярных выражений для повышения производительности.

Если патэрн начинается с .* и установлена опция PCRE_DOTALL, патэрн неявно заякоривается PCRE, поскольку он может совпасть только в начале строки-субъекта. Однако, если PCRE_DOTALL не установлена, PCRE не может выполнить эту оптимизацию, поскольку метасимвол . не совпадает тогда с символом новой строки/newline, и, если строку-субъект содержит newlines, патэрн может совпасть с символом, идущим непосредственно после одного из символов новой строки, вместо того чтобы совпадать только в самом начале. Например, патэрн

(.*) second

совпадает в субъекте "first\nand second" (где \n это символ новой строки) с первой захваченной подстрокой "and". Для этого PCRE пытается совпадать в начале после каждого символа новой строки в субъекте.

Если вы используете такой патэрн со строками-субъектами, которые не содержат newlines, наилучшая производительность будет достигнута установкой PCRE_DOTALL, или если начать патэрн с ^.* для явного указания заякоривания. Это удержит PCRE от необходимости сканировать субъект в поисках newline для рестарта с него.

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

(a+)*

Он может совпасть с "aaaa" 33 разными способами, и это количество увеличивается очень быстро по мере увеличения длины строки. (Повторение * может совпасть 0, 1, 2, 3 или 4 раза, и для каждого случая/case, отличного от 0, повторения + могут совпадать разное количество раз.)
Если остаток патэрна таков, что всё совпадение терпит неудачу, PCRE должно, в принципе, попытаться выполнить все возможные варианты, и это может потребовать огромного количества времени.

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

  (a+)*b
где следом идёт литеральный символ. Прежде чем полагаться на стандартную процедуру поиска совпадений, PCRE проверяет, имеется ли "b" далее в строке-субъекте, и если нет, совпадение немедленно завершается неудачей. Однако, когда последующего литерала нет, эта оптимизация не может быть использована. Почувствуйте разницу, сравнив поведение
  (a+)*\d
с поведением вышеприведённого патэрна. Первый выдаёт неудачу почти сразу, когда применяется к строке символов "a", а второй затрачивает значительное время на поиск в строках длиной более 20 символов.

Назад Оглавление Вперёд
Модификаторы ПатэрнаВверх preg_grep




HTML Site Map

Rambler's Top100
© 2009 Тест скорости интернета |  Boont.Ru