Описание синтаксиса Perl-совместимых регулярных выраженийСинтаксис регулярных выраженийВступление
Этот раздел описывает синтаксис и семантику регулярных выражений, которые поддерживает
модуль PCRE. Регулярные выражения также описывают документация
языка Perl и много других книг, отдельные содержат большое количество
примеров. Подробно регулярные выражения описывает книга
Джеффри Фридла «Регулярные выражения» (Jeffrey Friedl
“Mastering Regular Expressions”), опубликованная издательством O'Reilly
(ISBN 1-56592-257-3).
Описание здесь привели как справочную информацию.
Регулярное выражение — это шаблон, с которым входная строка сравнивается слева направо.
Большинство символов в шаблоне — буквально сами символы,
которые совпадают с символами во входной строке.
В качестве банального примера, шаблон
The quick brown fox
соответствует той части искомой строки, которая с ним совпадает.
Разделители
При работе с функциями модуля PCRE шаблон заключают
в разделители.
В качестве разделителя разрешены любые символы, кроме буквенно-цифровых, обратного
слеша или пробельных символов.
Парсер без предупреждения проигнорирует пробел перед корректным разделителем.
Разделители, которыми пользуются чаще: слеши (/),
знаки решётки (#) и тильды (~).
Следующий абзац приводит примеры шаблонов с корректными разделителями.
Скобки также указывают как разделители, где начальный разделитель —
открывающая скобка, а конечный разделитель —
закрывающая. Символы (), {},
[] и <> —
допустимые пары разделителей.
]]>
Разделители в виде скобок не нужно экранировать, если скобки также задали
как метасимволы в шаблоне, но как и с другими разделителями скобки нужно
экранировать, если их указывают непосредственно как символы.
Разделитель экранируют обратным слешем «\», если нужно указать разделитель
внутри шаблона. Если разделитель часто указывают в шаблоне, в целях удобочитаемости
выбирают другой разделитель для этого шаблона.
Функцией preg_quote пользуются, чтобы
экранировать строку в шаблоне, а необязательный второй параметр функции
разрешает задать разделитель.
После закрывающего разделителя указывают
модификаторы
шаблонов. Следующий пример показывает сопоставление без учёта регистра:
Метасимволы
Сила регулярных выражений состоит в том, что в шаблон можно включать подшаблоны
для альтернативного сопоставления и поиска повторений.
Альтернативы и повторения кодируют в шаблоне метасимволами,
которые парсер интерпретирует не как самих себя, а особым образом.
В коде метасимволы записывают двумя способами: одни парсер распознаёт за пределами
квадратных скобок, другие — внутри квадратных скобок. Вне квадратных скобок
распознаются следующие метасимволы:
Метасимволы вне квадратных скобокМетасимволОписание\общий экранирующий символ с несколькими назначениями^декларирует начало данных (или строки в многострочном режиме)$декларирует конец данных или до завершения строки (или окончание строки в многострочном режиме).соответствует любому символу, кроме перевода строки (по умолчанию)[начало описания символьного класса]конец описания символьного класса|начало ветки условного выбора(начало подшаблона)конец подшаблона?
расширяет смысл метасимвола (,
выступает в роли квантификатора, который означает 0 или 1 вхождение,
а также преобразовывает жадные квантификаторы в ленивые
(смотрите раздел «Повторение»)
*квантификатор, который означает 0 или более вхождений+квантификатор, который означает 1 или более вхождений{начало количественного квантификатора}конец количественного квантификатора
Часть шаблона в квадратных скобках называется
символьным классом.
В символьный класс входят только следующие метасимволы:
Метасимволы внутри квадратных скобок (символьные классы)
МетасимволОписание\общий экранирующий символ^означает отрицание класса, допустим только в начале класса-означает символьный интервал
Другие разделы детально описывают каждый из перечисленных метасимволов.
Экранирующие последовательности
У обратного слеша «\» несколько назначений. Главное — если обратный слеш
предшествует небуквенно-цифровому символу, обратный слеш снимает с этого символа
специальное значение, которое символ мог иметь. Обратный слеш как экранирующий символ
допустим как в символьном классе, так и вне него.
Например, для сопоставления символа «*» в шаблоне указывают «\*».
Такой подход применяют независимо от того,
интерпретировал бы парсер символ, который идёт за обратным слешем, как
специальный без экранирования или нет. Поэтому небуквенно-цифровые символы
безопасно экранировать символом обратного слеша «\», чтобы указать,
что символ означает сам себя. Для сопоставления
обратного слеша с самим собой, записывают «\\».
Обратный слеш в одинарных и двойных PHP-строках
работает как служебный символ. Поэтому когда нужно сопоставить символ \ с регулярным выражением \\,
в PHP-коде указывают "\\\\" или '\\\\'.
Если указали модификатор
PCRE_EXTENDED,
пробельные символы в шаблоне вне описания символьного класса парсер проигнорирует.
Также игнорируется часть строки, которая находится между символом «#»,
который, опять же, не участвует в описании символьного класса,
и следующим символом перевода строки. В этой ситуации обратный слеш указывают
как экранирующий символ для обозначения вхождений пробельных символов
или символа «#» в шаблоне.
Второе назначение обратного слеша — кодировать непечатаемые символы в шаблоне в видимой форме.
Нет ограничений на появление непечатаемых символов, кроме двоичного нуля,
который завершает шаблон, но при подготовке шаблона в текстовом редакторе
проще записывать следующие экранирующие последовательности, чем двоичный символ,
который последовательности представляют:
\aсимвол оповещения, сигнал, (BEL, шестнадцатеричный код 07)\cx«control + x», где x — произвольный символ\eescape (шестнадцатеричный код 1B)\fразрыв страницы (шестнадцатеричный код 0C)\nперевод строки (шестнадцатеричный код 0A)\p{xx}
символ со свойством xx, подробнее об этом рассказывает раздел
«Свойства Unicode-символов»
\P{xx}
символ без свойства xx, подробнее об этом рассказывает раздел
«Свойства Unicode-символов»
\rвозврат каретки (шестнадцатеричный код 0D)\Rразрыв строки: совпадает с \n, \r и \r\n\tтабуляция (шестнадцатеричный код 09)\xhh
символ с шестнадцатеричным кодом hh
\dddсимвол с восьмеричным кодом ddd, либо ссылка на подшаблон
Если быть более точным, комбинация «\cx» интерпретируется
следующим образом: если «x» — символ нижнего регистра,
он преобразовывается в верхний регистр.
После этого шестой бит символа (шестнадцатеричный код 40) инвертируется.
Таким образом, комбинация «\cz» интерпретируется как шестнадцатеричное
значение 1A, тогда как комбинация «\c{» получает шестнадцатеричное
значение 3B, а «\c;» — 7B.
После комбинации «\x» считываются ещё две шестнадцатеричные цифры
в нижнем или верхнем регистре. В режиме UTF-8 разрешена запись
«\x{...}», где содержимое скобок — строка из шестнадцатеричных цифр.
Такая запись интерпретируется как символ UTF-8, кодовый номер которого — шестнадцатеричное число.
Исходная шестнадцатеричная экранирующая последовательность \xhh
совпадает с двухбайтовым символом в кодировке UTF-8, если значение шестнадцатеричного числа
превышает 127.
После комбинации «\0» считываются ещё две восьмеричные цифры.
Если в записи менее двух цифр, считываются только те, которые указали.
Таким образом, последовательность «\0\x\07» будет интерпретирована
как два двоичных нуля, за которыми следует символ оповещения (звонок). Убедитесь, что
указали две цифры после начального нуля, если символ, который идёт за нулём, сам
восьмеричная цифра.
Обработка обратного слеша, за которым следует ненулевая цифра, несколько сложнее.
Вне символьного класса модуль PCRE воспринимает обратный слеш и цифры за ним
как десятичное число. Если полученное значение меньше десяти
или если шаблон содержит по меньшей мере такое же количество предшествующих
текущей позиции подшаблонов, парсер интерпретирует всю конструкцию как
ссылку на подшаблон.
Более детальное описание приведём ниже при обсуждении механизма работы подшаблонов.
Внутри символьного класса, либо если полученное значение больше 9 и соответствующее
количество предшествующих подшаблонов отсутствует, модуль PCRE считывает до трёх восьмеричных
цифр, которые следуют за обратным слешем, и генерирует один байт из последних 8-ми
значащих битов полученного значения. Все последующие цифры обозначают сами себя. Например:
\040ещё один способ записи пробела\40
то же самое, если данной записи предшествует менее сорока подшаблонов
\7всегда интерпретируется как ссылка на подшаблон\11
может быть как обратной ссылкой, так и альтернативной записью символа табуляции
\011всегда интерпретируется как символ табуляции\0113символ табуляции, за которым следует цифра «3»\113
интерпретируется как символ с восьмеричным кодом 113 (так как ссылок на подшаблоны не может быть больше 99)
\377байт, всецело состоящий из единичных битов\81
либо обратная ссылка, либо бинарный ноль, за которым следуют цифры «8» и «1»
Следует помнить, что восьмеричные значения, превышающие 100, следует писать без
лидирующего нуля, так как читается не более трёх восьмеричных цифр.
Все последовательности, определяющие однобайтное значение, могут встречаться
как внутри, так и вне символьных классов. Кроме того, внутри символьного класса
запись «\b» интерпретируется как символ возврата
(«backspace», шестнадцатеричный код 08). Вне символьного класса она имеет
другое значение (какое именно, описано ниже).
Третье использование обратного слеша — указание общего типа символов:
\dлюбая десятичная цифра\Dлюбой символ, кроме десятичной цифры\hлюбой горизонтальный пробельный символ\Hлюбой символ, не являющийся горизонтальным пробельным символом\sлюбой пробельный символ\Sлюбой непробельный символ\vлюбой вертикальный пробельный символ\Vлюбой символ, не являющийся вертикальным пробельным символом\wЛюбой символ, образующий «слово»\WЛюбой символ, не образующий «слово»
Каждая пара таких специальных последовательностей делит полное множество
всех символов на два непересекающихся множества.
Любой символ соответствует одному и только одному множеству из пары.
Следующие символы считаются «пробельными»: HT (9), LF (10), FF (12), CR (13)
и пробел (32). Однако, если идёт сопоставление с учётом локали, символы
с кодовыми точками в диапазоне 128-255 также рассматриваются как пробельные,
например, символ неразрывного пробела NBSP (A0).
Символ, образующий «слово» — это произвольная цифра, буква или символ подчёркивания,
проще говоря, любой символ, которому разрешено быть частью
«слова» в Perl. Определение букв и цифр управляется
символьными таблицами, с которыми собрали модуль PCRE. И, как следствие, эти наборы
могут отличаться в различных локализированных дистрибутивах.
Например, в локали «fr» (Франция) ряд символов с кодом выше 128 применяют
для записи ударных символов и поэтому они соответствуют маске \w.
Описанные типы символов применяют как внутри, так и вне символьных
классов, и типы соответствуют одному символу данного типа. Если текущая точка
сравнения находится в конце строки, ни один из них не сможет совпасть, поскольку
нет символа, с которым могло бы произойти совпадение.
Четвёртое назначение обратного слеша — определение некоторых формальных утверждений,
которые описывают условия месторасположения особых позиций в строке
и не затрагивают сами символы. Работу с подшаблонами как с более
сложными формальными утверждениями, документация описывает ниже.
К такими управляющим последовательностям относятся:
\bграница слова\Bне является границей слова\Aначало данных (независимо от многострочного режима)\Z
конец данных либо позиция перед последним переводом строки (независимо от многострочного режима)
\zконец данных (независимо от многострочного режима)\Gпервая совпадающая позиция в строке
Описанные выше последовательности не могут встречаться в символьных классах
(исключая комбинацию «\b», которая внутри класса означает
символ возврата «backspace»).
Границей слова считается такая позиция в строке, в которой из текущего и
предыдущего символа только один соответствует \w или
\W (т.е. один из них соответствует \w,
а другой \W). Начало или конец строки также соответствуют
границе слова Если первый или, соответственно, последний символ совпадает
с \w.
Утверждения \A, \Z и \z
отличаются от традиционных метасимволов начала и конца строки строки —
циркумфлекса ^ и знака доллара $, которые описывает раздел
«Якоря»,
тем, что они всегда совпадают только в самом начале и конце входной строки,
какие бы параметры ни установили.
На них никак не влияют опции
PCRE_MULTILINE и
PCRE_DOLLAR_ENDONLY.
Разница между \Z и \z в том,
что \Z соответствует позиции перед последним символом,
если последний символ — перевод строки, кроме самого конца строки.
Тогда как \z соответствует исключительно концу данных.
Утверждение \G будет истинным, только если
текущая проверяемая позиция находится в начале совпадения, которое указали
в параметре offset функции preg_match.
Она отличается от последовательности \A при ненулевом значении
параметра offset.
Последовательности \Q и \E
игнорируют метасимволы регулярного выражения в шаблоне. Например:
шаблон \w+\Q.$.\E$ совпадёт с «заякоренными»
в конце строки одним или более символами слова, за которыми следуют
литералы .$.. Обратите внимание, что это не меняет поведение
разделителей; например, шаблон #\Q#\E#$
неправильный, потому что второй символ # отмечает конец шаблона,
а последовательность \E# парсер интерпретирует как недопустимые модификаторы.
Последовательность \K сбрасывает начало совпадения. Например,
шаблон foo\Kbar совпадёт со значением «foobar», но сообщит,
что совпал только с «bar». Последовательность
\K не мешает установке подшаблонов. Например, если шаблон
(foo)\Kbar совпадёт со строкой «foobar», первым подшаблоном
всё равно будет значение «foo».
Свойства Unicode-символов
С версии стандарта Unicode 5.1.0 при выборе режима UTF-8
доступны три дополнительные управляющие последовательности для соответствия
общим типам символов. Вот они:
\p{xx}символ со свойством xx\P{xx}символ без свойства xx\Xрасширенная последовательность Unicode
Имена свойств, которые выше представили символами xx, ограничены
общими категориями свойств Unicode. У каждого символа есть ровно одно такое свойство,
которое обозначено двухбуквенной аббревиатурой. Для совместимости с Perl отрицание
можно указать, включив символ циркумфлекса «^» между открывающей скобкой и именем свойства.
Например, \p{^Lu} — это то же самое, что и \P{Lu}.
Если с управляющей последовательностью \p
или \P указали только одну букву,
она включает все свойства, которые начинаются с этой буквы.
В этом случае при отсутствии отрицания фигурные скобки необязательны;
следующие два примера эквивалентны:
Модуль PCRE не поддерживает расширенные свойства наподобие музыкальных символов
(InMusicalSymbols).
Указание регистронезависимого (безрегистрового) режима не влияет на эти
управляющие последовательности. Например, запись \p{Lu} всегда
совпадает только с прописными буквами.
Наборы Unicode-символов описываются как те, что принадлежат конкретным сценариям.
Любой символ из этих наборов сопоставим с помощью имени сценария.
Например:
\p{Greek}\P{Han}
Символы, которые не принадлежат конкретному сценарию, объединяются
в сценарий Common. Текущий список сценариев:
Последовательность \X соответствует кластеру расширенных Unicode-графем.
Расширенный графемный кластер — один или несколько Unicode-символов,
которые объединяются в один символьный знак (глиф). По сути, его можно рассматривать
как Unicode-эквивалент для ., поскольку он находит один независимый
комплексный символ, независимо от того, сколько отдельных символов нужно для его отрисовки.
Для версий PCRE до 8.32 (что соответствует версиям PHP до 5.4.14 при работе
со встроенным модулем PCRE), последовательность \X равносильна
записи (?>\PM\pM*). Таким образом, он ищет символы без
свойства «mark», и рассматривает последовательность как атомарную группу (см ниже).
Символы со свойством «mark» обычно являются отличительными признаками, которые влияют на предыдущий символ.
Совпадение символов по Unicode-свойству — не быстрая операция,
поскольку модулю PCRE приходится выполнить поиск в структуре данных,
которая содержит более пятнадцати тысяч символов. Поэтому традиционные управляющие
последовательности в модуле PCRE, например \d и \w,
не используют Unicode-свойства.
Якоря
Метасимвол начала строки ^ вне символьного класса
по умолчанию соответствует началу обрабатываемых данных,
если не установили модификаторы. Внутри символьного класса метасимвол
^ несёт совершенно другое значение.
Циркумфлекс ^ не обязан быть первым символом
шаблона, если в шаблоне задействовали несколько альтернатив,
но должен быть первым символом в каждой альтернативе, в которой
он появляется, если шаблон когда-либо будет соответствовать этой ветви.
Если все альтернативы начинаются с циркумфлекса ^,
то есть шаблон ограничен совпадением только с началом входной строки,
говорят что шаблон «заякорен». Есть и другие способы «заякорить» шаблон.
Знак доллара $ — утверждение, которое истинно только тогда,
когда текущая точка сопоставления находится в конце входной строки, или
непосредственно перед последним символом, в случае если последний символ —
перевод строки, если не указали модификаторы.
Метасимвол конца строки $ не обязан быть последним символом шаблона,
если в шаблоне задействовали несколько альтернатив, но должен быть последним символом
в каждой альтернативе, в которой он фигурирует. Внутри символьного класса
символ «$» не несёт специального значения.
Поведение метасимвола конца строки изменяют модификатором
PCRE_DOLLAR_ENDONLY так,
чтобы метасимвол соответствовал исключительно концу строки. Данный флаг никак не
касается специальной последовательности \Z.
Значение метасимволов начала и конца строки меняется, если установили
модификатор PCRE_MULTILINE.
В этой ситуации, кроме совпадений в начале или в конце строки,
метасимволы «^» и «$» соответствуют позиции непосредственно после символа
перевода строки «\n». Например, шаблон /^abc$/ встречается в строке «def\nabc»
в многострочном режиме и не встречается в нормальном режиме.
Таким образом, шаблон который «заякорен» в однострочном режиме, все ветки которого
начинаются с циркумфлекса «^», не будет признан «заякоренным» в многострочном режиме.
Парсер игнорирует модификатор PCRE_DOLLAR_ENDONLY,
если установили модификатор PCRE_MULTILINE.
Следует заметить, что служебные последовательности \A, \Z и \z
указывают для сопоставления с началом или концом строки в обоих
режимах. И если все ветви шаблона начинаются с \A, шаблон будет «заякорен»
независимо от присутствия модификатора
PCRE_MULTILINE.
Метасимвол точка
Вне символьного класса точка соответствует любому (в том числе и непечатаемому,
бинарному) символу, кроме символа перевода строки «\n» (в обычном режиме).
Если задан модификатор
PCRE_DOTALL,
точка соответствует также символу перевода строки.
Обработка метасимвола «точка» никак не связана с метасимволами начала и конца
строки, единственное, что у них общего, — так это специальное отношение
к символу перевода строки. Внутри символьного класса точка не имеет
специального значения.
Для совпадения с одним байтом можно пользоваться последовательностью символов \C.
Это может быть полезно в режиме UTF-8, так как с точкой
в этом режиме будет совпадать целый символ, который может состоять
из нескольких байтов.
Символьные классы
Открывающая квадратная скобка объявляет начало символьного класса,
который завершают квадратной скобкой. Символ «]» не имеет специального
значения, и если закрывающая квадратная скобка необходима как член
символьного класса, она должна быть первым символом непосредственно после
открывающей квадратной скобки (если указан метасимвол «^», то
непосредственно после него), либо экранироваться обратным слешем.
Символьный класс соответствует одиночному символу входной строки;
символ должен входить в набор символов, который определили в классе,
если только первый символ в классе не циркумфлекс «^»,
тогда символ входной строки не должен входить в класс. Если циркумфлекс «^»
нужен как член класса, проверяют, чтобы он не шёл первым символом в описании класса,
либо экранируют циркумфлекс обратным слешем.
Символьный класс [aeiou], например, соответствует любой гласной букве
в нижнем регистре, тогда как класс [^aeiou] соответствует любому согласному
символу нижнего регистра.
Обратите внимание, что циркумфлекс — просто удобный способ определить символьный
класс за счёт перечисления тех символов, которые не должны входить в класс.
Символ начала строки — не утверждение: он по-прежнему использует символ из входной строки
и завершается ошибкой, если текущий указатель находится в конце строки.
При регистронезависимом сопоставлении
буквы символьного класса соответствует версии символа как в верхнем,
так и в нижнем регистре. Поэтому символьный класс [aeiou]
соответствует как букве «A», так и букве «a».
Аналогично, класс [^aeiou] не соответствует ни «A», ни «a», тогда как в
регистрозависимом режиме совпадение бы состоялось.
Внутри символьного класса у символа перевода строки «\n» нет специального
значения, независимо от наличия модификаторов
PCRE_DOTALL и
PCRE_MULTILINE.
Символьные классы, построенные на отрицании, например [^a], всегда
соответствуют символу перевода строки.
Символ минус «-» (дефис) внутри класса используется для задания
символьного диапазона. Например, [d-m] соответствует любому символу,
находящемуся между «d» и «m», включая сами символы «d» и «m».
Если «-» необходим, как член класса,
он должен находиться в такой позиции, в которой он не может интерпретироваться
как диапазон (как правило, это первый и последний символ описания класса),
либо экранироваться при помощи обратного слеша.
Недопустимо записывать закрывающую квадратную скобку «]» как границу
символьного диапазона. Например, парсер интерпретирует шаблон «[W-]46]»
как символьный класс, который состоит из двух символов: «W» и «-», за которыми
идёт строка «46]», поэтому шаблон будет соответствовать
строкам «W46]» или «-46]».
Чтобы всё же задать символ «]» в описании диапазона, его нужно
заэкранировать обратным слешем, например, парсер интерпретирует
шаблон [W-\]46] как символьный класс, который состоит из символьного диапазона
вместе с двумя последующими символами «4» и «6».
Такого же результата можно достичь через шестнадцатеричное
или восьмеричное представление символа «]».
Диапазоны символьных классов определяют последовательностью ASCII-символов,
которые указывают через символ «-». Диапазоны также разрешено определять числами,
например [\000-\037]. Диапазон будет соответствовать буквам в нижнем и верхнем регистрах,
если в диапазон включили буквы и установили регистронезависимое сопоставление. Например,
диапазоны [W-c] и [][\^_`wxyzabc] эквивалентны, парсер сопоставляет символы
без учёта регистра, а если установлена таблица символов для французской локали «fr»,
парсер будет сопоставлять символы из диапазона [\xc8-\xcb] ударному «E» в обоих регистрах.
Типам символов \d, \D, \s, \S, \w и \W также разрешено присутствовать
в символьных классах и добавлять символы, которые им соответствуют, в класс.
Например, класс [\dABCDEF] соответствует
любой шестнадцатеричной цифре. Символ «^» указывают с типами символов
в верхнем регистре, чтобы указать более узкий набор символов. Например,
класс [^\W_] соответствует любой букве или цифре, но не символу подчёркивания.
Все небуквенно-цифровые символы, кроме \, -, ^ в начале и символа «]» в конце,
не относятся к специальным символам в символьном классе, но экранирующий
слеш перед ними не навредит. Символ конца шаблона — всегда специальный символ
и должен быть заэкранирован внутри выражения.
Язык Perl поддерживает нотацию POSIX для символьных классов. Это включает
имена в квадратных скобках: [: и :].
Модуль PCRE также поддерживает эту тип записи. Например, шаблон
[01[:alpha:]%] совпадёт с «0», «1», любым алфавитным символом
или символом «%». Модуль PCRE поддерживает следующие имена классов:
Символьные классыalnumбуквы и цифрыalphaбуквыasciiсимволы с кодами 0–127blankтолько пробел или символ табуляцииcntrlуправляющие символыdigitдесятичные цифры (то же самое, что и \d)graphпечатные символы, исключая пробелlowerстрочные буквыprintпечатные символы, включая пробелpunctпечатные символы, исключая буквы и цифрыspaceпробельные символы(почти то же самое, что и \s)upperпрописные буквыwordсимволы «слова» (то же самое, что и \w)xdigitшестнадцатеричные цифры
Класс пробельных символов (space) — это горизонтальная табуляция (HT, 9),
перевод строки (LF, 10), вертикальная табуляция (VT, 11), разрыв страницы (FF, 12),
возврат каретки (CR, 13) и пробел (32). Учтите, что этот список включает
вертикальную табуляцию (VT, код 11). Это отличает «space» от \s,
который не включает этот символ (для совместимости с Perl).
Название word — это модуль Perl, а blank —
модуль GNU, начиная с версии Perl 5.8. Другой модуль Perl —
отрицание, которое указывается символом ^ после
двоеточия. Например, [12[:^digit:]] совпадёт с «1», «2»,
или с любой не-цифрой.
В режиме UTF-8 символы со значениями, которые превышают 128, не совпадут ни с одним
из символьных классов POSIX.
Начиная с libpcre 8.10 некоторые символьные классы изменили, чтобы
использовать свойства символов Unicode, в этом случае упомянутое ограничение не применяется.
Подробнее об этом рассказывает руководство PCRE(3).
Свойства символов Unicode могут возникнуть внутри символьного класса.
Они не могут быть частью диапазона. Символ минус (дефис), после символьного
класс Unicode будет совпадать буквально. Попытка закончить диапазон
свойством символа Unicode вызовет предупреждение.
Альтернативный выбор
Символ вертикальной черты «|» указывают для разделения альтернативных масок.
Например, шаблон gilbert|sullivan соответствует как
«gilbert», так и «sullivan». Допустимо указывать любое количество альтернатив,
также допустимо указывать пустые альтернативы (соответствуют пустой строке).
В процессе поиска соответствия просматриваются все перечисленные альтернативы
слева направо, останавливаясь после первого найденного соответствия.
Если альтернативные варианты перечислили в подшаблоне, то весь шаблон совпадёт
только при соответствии одного из альтернативных вариантов подшаблона
и остатка основного шаблона.
Установка внутренних опций
Установки модификаторов PCRE_CASELESS,
PCRE_MULTILINE,
PCRE_DOTALL,
PCRE_UNGREEDY,
PCRE_EXTRA,
PCRE_EXTENDED
и PCRE_DUPNAMES, которые влияют на шаблон глобально, переопределяют внутри шаблона
буквами внутренних опций языка Perl, которые указывают между символами «(?» и «)». Буквы опций:
Буквы внутренних опцийiPCRE_CASELESSmPCRE_MULTILINEsPCRE_DOTALLxPCRE_EXTENDEDUPCRE_UNGREEDYXPCRE_EXTRA (не поддерживается с PHP 7.3)JPCRE_INFO_JCHANGED
Шаблон (?im), например, устанавливает регистронезависимое многострочное сопоставление.
Перед опцией, которую нужно сбросить, ставят символ «-» или комбинируют
установку и отмену режимов. Запись (?im-sx), например, устанавливает флаги
PCRE_CASELESS,
PCRE_MULTILINE
и отменяет флаги PCRE_DOTALL
и PCRE_EXTENDED.
Модуль отменит опцию, если символ расположили одновременно до и после
символа «-».
Парсер применит изменение к оставшейся части шаблона,
если опцию изменяют на верхнем уровне (т. е. вне круглых скобок подшаблона).
Поэтому шаблон /ab(?i)c/ совпадёт только со значениями «abc» и «abC».
Эффект будет другим, если опцию изменяют внутри подшаблона.
Это изменение поведения в 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) отличается тем, что должен быть расположен в шаблоне прежде, чем будет
установлена любая другая дополнительная возможность, даже если его расположили
на верхнем уровне. Рекомендовано размещать флаг (?X) в самом начале шаблона.
Подшаблоны
Подшаблоны ограничивают круглыми скобками, которым разрешено быть вложенными.
Пометка части шаблона как подшаблона выполняет две функции:
Локализирует набор альтернатив. Например, шаблон
cat(aract|erpillar|) соответствует одному из слов «cat»,
«cataract» или «caterpillar». Без скобок он соответствовал
бы строкам «cataract», «erpillar» или пустой строке.
Настраивает подшаблон как подшаблон захвата подстроки, как показывает первый пункт.
Функция модуля PCRE pcre_exec через аргумент ovector
возвращает вызывающей функции подстроку, которая соответствует подшаблону,
когда совпадает полный шаблон.
Функция подсчитывает открывающие круглые скобки с единицы слева направо,
чтобы получить количество захватывающих подшаблонов.
Например, если строка «the red king» сопоставляется с шаблоном
the ((red|white) (king|queen)),
парсер захватит подстроки «red king», «red» и «king» с номерами 1, 2 и 3
соответственно.
В реальной жизни выполнение одновременно двух функций может оказаться неудобным.
Бывают случаи, когда нужна группировка альтернатив без захвата строки.
Если после открывающей круглой скобки идут символы «?:», захват строки
не происходит, и текущий подшаблон не нумеруется.
Например, если строка «the white queen» сопоставляется с шаблоном
the ((?:red|white) (king|queen)),
парсер захватит подстроки «white queen» и «queen» и пронумерует подстроки числами
1 и 2 соответственно. Максимальное количество захватывающих подшаблонов — 65 535.
Такие большие шаблоны могут не скомпилироваться, в зависимости от настроек модуля libpcre.
Если в незахватывающем подшаблоне нужно указать дополнительные
опции, пользуются удобным сокращением: символ, который обозначает
устанавливаемую опцию, помещается между «?» и «:».
Таким образом, следующие два шаблона
соответствуют одному и тому же набору строк. Поскольку альтернативные
версии берутся слева направо, и установленные опции сохраняют своё
действие до конца подшаблона, опция, установленная в одной ветке, также
имеет эффект во всех последующих ветках. Поэтому приведённые шаблоны
совпадают как с «SUNDAY», так и с «Saturday».
Именованные подшаблона задают синтаксисом
(?P<name>pattern). Этот подшаблон будет индексирован
в массиве совпадений кроме обычного числового индекса, ещё и по имени name.
Именованные подшаблоны записывают двумя альтернативными синтаксисами:
(?<name>pattern) и (?'name'pattern).
Иногда необходимо иметь несколько совпадений, которые исключают друг друга.
Обычно, каждое такое совпадение получает свой собственный номер, даже
если шаблон разрешает совпасть только одному из них.
Синтаксис (?| даёт обойти это поведение и убрать
дублирующиеся номера. Рассмотрим следующее регулярное выражение,
сопоставленное со строкой Sunday:
Здесь парсер сохраняет значение Sun в ссылке 2, тогда как
ссылка 1 пуста. В результате сопоставления Saturday
в обратной ссылке 1 появляется Sat,
в то время как обратная ссылка 2 не существует.
Запись (?| в шаблоне решает эту проблему:
В этом шаблоне оба подшаблона Sun и Sat
парсер сохранят под номером 1.
Повторение
Повторение определяется квантификаторами, которые идут за любым из указанных
элементов:
одним символом, возможно, экранированнымметасимволом «точка»символьным классомссылкой на предыдущий фрагмент шаблона (смотрите следующий раздел)взятым в круглые скобки подшаблоном (если это не утверждение — смотрите далее)
Общий квантификатор повторения указывает минимальное и максимальное
допустимое количество совпадений, согласно двум числам
в фигурных скобках, разделённым запятой. Числа должны быть меньше чем 65 536,
и первое число не должно превышать второе по значению.
Например:
z{2,4}
соответствует «zz», «zzz» или «zzzz». Закрывающая фигурная скобка сама
по себе — не специальный символ. Если второе число опустили,
но запятую поставили, нет верхнего предела; Если и второе
число, и запятую опустили, требуется точное число повторений.
Поэтому шаблон
[aeiou]{3,}
соответствует как минимум трём последовательным гласным, а также любому
количеству гласных выше трёх, тогда как шаблон
\d{8}
соответствует ровно восьми цифрам. Открывающая фигурная скобка,
расположенная в недопустимой для квантификатора позиции, либо не
соответствующая синтаксису квантификатора, интерпретируется как
обыкновенная символьная строка. Например, {,6} — не квантификатор,
а интерпретируется как символьная строка из четырёх символов.
Квантификатор {0} — допустим и ведёт себя таким образом, будто бы
сам квантификатор и предшествующий ему элемент отсутствуют.
Для удобства, а так же обратной совместимости, у трёх наиболее распространённых
квантификатора односимвольные аббревиатуры:
Можно конструировать бесконечные циклы, указав после шаблона, который совпадает
с пустой строкой, квантификатор без верхнего предела, например:
(a?)*
Ранние версии языка Perl и модуля PCRE выдавали ошибку во время компиляции для таких
шаблонов. Однако, поскольку бывают случаи, когда подобные шаблоны могли бы
быть полезны, была добавлена поддержка таких шаблонов. Но если любое повторение
такого подшаблона фактически не совпадает ни с какими символами, цикл
принудительно прерывается.
По умолчанию все квантификаторы — «жадные», это означает, что они
совпадают максимально возможное количество раз (но не более, чем максимально
допустимое), и не останавливают сопоставление
остальных частей шаблона. Классический пример проблем, которые
могут возникнуть в связи с такой особенностью квантификаторов, —
попытка найти комментарии в программах, которые написали на языке C.
Комментарием признаётся произвольный текст внутри символьных комбинаций /* и */
(при этом, символы «/» и «*» тоже могут быть частью комментария). Попытка найти комментарии
через шаблон
/\*.*\*/
в строке
/* первый комментарий */ не комментарий /* второй комментарий */
закончится неудачей, поскольку указанный шаблон соответствует всей строке
целиком (из-за жадности квантификатора «*»).
Однако, если сразу же после квантификатора идёт вопросительный знак, квантификатор
становится «ленивым» и соответствует минимально допустимому количеству раз.
Поэтому шаблон
/\*.*?\*/
корректно находит комментарии языка C. Знак «?» после
квантификатора влияет только на его жадность, и не влияет
на другие свойства. Не нужно путать
квантификатор «?» (ноль или одно соответствие) ограничитель
жадности. Из-за его двойственности пользуются
следующей записью:
\d??\d,
которая, в первую очередь, соответствует одной цифре, но также
может соответствовать и двум цифрам, если это необходимо для
соответствия остальных частей шаблона.
Если установили опцию
PCRE_UNGREEDY
(которой нет в языке Perl), квантификаторы перестают быть жадными по умолчанию,
но могут становиться жадными, если за ними идёт символ «?».
Говоря другими словами, знак вопроса инвертирует жадность
квантификаторов.
Квантификаторы, за которыми идёт символ +, — «захватывающие».
Они поглощают столько символов, сколько могут, и не возвращаются обратно
для совпадения остатка шаблона. Поэтому шаблон .*abc
совпадёт с «aabc», а .*+abc — нет, потому что
подшаблон .*+ захватит всю строку целиком. Захватывающими
квантификаторами пользуются для ускорения обработки.
Если задали подшаблон с квантификатором, для которого установили минимальное
количество повторений (больше одного), либо задали максимальное количество
повторений, то для откомпилированного шаблона требуется больше памяти
(пропорционально минимуму либо максимуму соответственно).
Если шаблон начинается с символов .* либо .{0,} и установили модификатор
PCRE_DOTALL
(аналог Perl-опции /s), который разрешает метасимволу
«точка» соответствовать переводам строк, шаблон неявно заякоривается.
Это происходит потому, что все последующие конструкции парсер будет сопоставлять
с каждой символьной позицией в обрабатываемом тексте, и, как следствие,
начало строки — единственная позиция, которая даёт наиболее полное совпадение.
Модуль PCRE рассматривает каждый такой шаблон, как если бы ему предшествовала
последовательность \A. Если известно, что данные
не содержат переводов строк, а сам шаблон начинается с символов .*, рекомендуется
использовать модификатор PCRE_DOTALL
для оптимизации шаблона, либо указывать метасимвол «^» для явного заякоривания.
Если захватывающий подшаблон повторяется, результирующим значением
подшаблона будет подстрока, которая совпадает с результатом последней итерации.
Например, когда шаблон
(tweedle[dume]{3}\s*)+
совпадёт со значением «tweedledum tweedledee», результирующим значением подшаблона
будет значение «tweedledee». Однако, если присутствуют вложенные захватывающие
подшаблоны, значения, которые соответствуют этим подшаблонам, допускается устанавливать
в предыдущих итерациях. Например, когда шаблон
/(a|(b))+/
совпадёт со значением «aba», значением второй захваченной подстроки будет значение «b».
Обратные ссылки
Вне символьного класса модуль интерпретирует обратный слеш, за которым идёт
цифра больше 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», хотя сам
подшаблон сопоставляется без учёта регистра.
На один и тот же подшаблон разрешено устанавливать несколько ссылок. Если
подшаблон не участвовал в сопоставлении, то сопоставление со
ссылкой на неё всегда терпит неудачу. Например, шаблон
(a|(bc))\2
терпит неудачу, если находит соответствие с «a» раньше, чем с «bc».
Поскольку может быть до 99 обратных ссылок, все цифры, следующие
за обратным слешем, рассматриваются как часть потенциальной
обратной ссылки. Если за ссылкой должна следовать цифра, необходимо
использовать ограничитель. Если указан флаг
PCRE_EXTENDED,
ограничителем может быть любой пробельный символ. В противном
случае можно использовать пустой комментарий.
Ссылка на подшаблон, внутри которого она расположена, терпит неудачу,
если это первое сопоставление текущего подшаблона. Например, шаблон (a\1)
не соответствует ни одной строке. Но всё же такие ссылки бывают
полезны в повторяющихся подшаблонах. Например, шаблон
(a|b\1)+
совпадает с любым количеством «a», «aba», «ababaa»... При
каждой итерации подшаблона обратная ссылка соответствует той части
строки, которая была захвачена при предыдущей итерации.
Чтобы такая конструкция работала, шаблон должен быть построен так,
чтобы при первой итерации сопоставление с обратной ссылкой не производилось.
Этого можно достичь, используя альтернативы (как в предыдущем
примере), либо квантификаторы с минимумом, равным нулю.
Управляющую последовательность \g
указывают для абсолютных и относительных ссылок на подшаблоны.
После этой последовательности должно быть указано
беззнаковое или отрицательное число, при желании заключённое в фигурные
скобки. Последовательности \1, \g1
и \g{1} эквивалентны друг другу.
Использование этого шаблона с беззнаковым числом поможет избежать
двусмысленности, присущей числам после обратного слеша. Это также
помогает отличить обратные ссылки от символов в восьмеричном
формате, а также упрощает запись числового литерала сразу после
обратной ссылки, например, \g{2}1.
Использование отрицательных чисел с \g полезно при
использовании относительных ссылок. Например, (foo)(bar)\g{-1}
соответствует «foobarbar», а (foo)(bar)\g{-2}
соответствует «foobarfoo». Это также может быть полезно в длинных
шаблонах, как альтернатива отслеживания числа подшаблонов,
на которые можно ссылаться в последующей части шаблона.
Обратную ссылку на именованный подшаблон указывают следующими записями:
(?P=name),
\k<name>, \k'name', \k{name},
\g{name}, \g<name> или \g'name'.
Утверждения
Утверждения — проверка символов, которые идут до или после текущей позиции
сопоставления, которая фактически не включает никаких символов (символы входной
строки не сопоставляются с утверждениями). Наиболее простые варианты утверждений,
например \b, \B, \A, \Z, \z, ^ и $, рассматривает раздел
«Экранирующие последовательности».
Более сложные утверждения записываются как подшаблоны. Утверждения
бывают двух видов: те, которые анализируют текст, следующий
после текущей позиции (look ahead),
и идущий перед ней (look behind).
Сопоставление подшаблонов, которые содержат утверждение, выполняют обычным
образом, за исключением того, что текущая позиция не изменяется.
Утверждения касательно последующего текста
начинаются с (?= для положительных утверждений и с (?! для отрицающих
утверждений. Например,
\w+(?=;)
совпадает со словом, за которым следует символ «;», но при этом сама
точка с запятой в совпадение не включается. А
foo(?!bar)
соответствует любому появлению «foo», после которого не идёт «bar».
Заметим, что похожий шаблон
(?!foo)bar
не будет искать вхождение «bar», которому предшествует любая
строка за исключением «foo». Но, тем не менее, он будет соответствовать
любым вхождениям подстроки «bar», поскольку условие (?!foo) всегда
&true;, если следующие три символа — «bar». Для получения желаемого
результата необходимо воспользоваться второй категорией утверждений.
Утверждения касательно предшествующего текста
начинаются с (?<= для положительных утверждений и (?<!
для отрицающих. Например,
(?<!foo)bar
найдёт вхождения «bar», которым не предшествует «foo». Сами
утверждения «назад» ограничены так, чтобы все подстроки, которым
они соответствуют, имели фиксированную длину. Но, Если
используются несколько альтернатив, они не обязаны иметь одинаковую
длину. Таким образом шаблон
(?<=bullock|donkey)
корректен, но
(?<!dogs?|cats?)
вызовет ошибку во время компиляции. Ветки, которые соответствуют
строкам разной длины, разрешены только на верхнем уровне утверждений
касательно предшествующего текста. Это расширение относительно
Perl 5.005, который требует чтобы все ветки соответствовали строкам
одинаковой длины. Такое утверждение как
(?<=ab(c|de))
не корректно, поскольку верхний уровень маски может соответствовать
строкам разной длины, но его можно преобразовать к корректному шаблону,
используя альтернативы на верхнем уровне:
(?<=abc|abde)
Утверждения касательно предшествующего текста реализованы так,
что для каждой альтернативы текущая позиция временно переносится
назад, на фиксированную ширину, после чего выполняется поиск
соответствия условию. Если перед текущей позицией недостаточно
символов, поиск соответствия терпит неудачу. Утверждения назад в сочетании
с однократными подшаблонами удобны для поиска
в конце строки; соответствующий пример приведён в конце раздела
«Однократные подшаблоны».
Несколько утверждений (разных типов) могут присутствовать в
утверждении, например:
(?<=\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 захватывающих подшаблонов.
Однократные подшаблоны
Как для минимального, так и для максимального количества повторений,
если следующая часть шаблона при сопоставлении завершается неудачно,
начинается повторный анализ повторяемого выражения на предмет того,
возможно ли успешное сопоставление всего шаблона при другом количестве
повторений. Бывают случаи, когда необходимо изменить описанную логику
работы для реализации специфического сопоставления либо оптимизации шаблона
(если автор уверен, что других вариантов соответствия нет).
В качестве примера, рассмотрим шаблон \d+foo в применении к строке
123456bar
После того, как \d+ будет сопоставлен с первыми шестью цифрами,
сопоставление «foo» потерпит неудачу. После этого, в соответствие
\d+, будет сопоставлено 5 цифр, после очередной неудачи будет сопоставлено
4 цифры и так далее. В конце концов весь шаблон потерпит неудачу.
Однократные подшаблоны указывают, что если одна часть шаблона была
сопоставлена, её не стоит анализировать повторно. Применимо к приведённому
выше примеру весь шаблон потерпел бы неудачу после первого же
неудачного сопоставления с «foo». Записываются однократные шаблоны
при помощи круглых скобок следующим образом: (?>. Например:
(?>\d+)bar
Этот вид подшаблона предотвращает повторный анализ подшаблона, если
сопоставление последующих элементов терпит неудачу. Однако это не мешает
повторно анализировать любые другие элементы, в том числе те, которые предшествуют
однократному подшаблону.
Говоря другими словами, подшаблоны такого типа соответствуют
той части подстроки, которой соответствовала бы одиночная
изолированная маска, заякоренная на текущей позиции обрабатываемого
текста.
Однократные подшаблоны — незахватывающие. Простые примеры,
подобные приведённому, можно охарактеризовать как безусловный
захват максимального количества повторений. В то время как
\d+ и \d+? корректируются так, чтобы остальные части шаблона
так же совпали, (?>\d+) соответствует исключительно максимальной по
длине последовательности цифр, даже если это приводит к неудаче при
сопоставлении других частей шаблона.
Однократным подшаблонам разрешено включать в себя более сложные конструкции
и быть вложенными.
Однократные подшаблоны разрешено указывать совместно с утверждениями, которые
касаются предыдущего текста для описания эффективных сопоставлений
в конце обрабатываемого текста. Рассмотрим простой шаблон
abcd$
в применении к длинному тексту, который не соответствует указанной маске.
Поскольку поиск происходит слева направо, вначале PCRE будет
искать букву «a», и только потом анализировать следующие
записи в шаблоне. Если шаблон указан в виде
^.*abcd$.
В этой ситуации вначале .* сопоставляется со всей строкой, после
чего сопоставление терпит неудачу (так как нет последующего символа «a»).
После чего .* сопоставляется со всей строкой, кроме последнего символа,
потом кроме двух последних символов, и так далее. В конечном итоге
поиск символа «a» происходит по всей строке. Однако, если шаблон записать
в виде:
^(?>.*)(?<=abcd)
повторный анализ для .* не выполняется, и, как следствие, может
соответствовать только всей строке целиком. После чего утверждение
проверяет последние четыре символа на совпадение с «abcd», и в случае
неудачи все сопоставление терпит неудачу. Для больших объёмов
обрабатываемого текста этот подход имеет значительный выигрыш во времени
выполнения.
Если шаблон содержит неограниченное повторение внутри подшаблона,
который в свою очередь также может повторяться неограниченное количество
раз, однократные подшаблоны помогают
избегать многократных неудачных сопоставлений,
которые длятся достаточно продолжительное время. Шаблон
(\D+|<\d+>)*[!?]
соответствует неограниченному количеству подстрок, которые состоят
не из цифр, либо из цифр заключённых в <>, за которыми следует
? либо !. Если в обрабатываемом тексте содержатся
соответствия, время работы регулярного выражения будет невелико.
Но если его применить к строке
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
это займёт длительное время. Это связанно с тем, что строка
может быть разделена между двумя частями шаблона многими способами,
и все они будут опробованы (в примере мы использовали [?!], поскольку
в случае одиночного символа в конце шаблона и PCRE и Perl выполняют
оптимизацию. Они запоминают последний одиночный символ и в случае
его отсутствия выдают неудачу). Если изменить шаблон на
((?>\D+)|<\d+>)*[!?],
не цифровые последовательности не могут быть разорваны, и
невозможность сопоставления обнаруживается гораздо быстрее.
Условные подшаблоны
В модуле PCRE реализована возможность подчинять шаблон условию или выбирать
из двух условных подшаблонов, в зависимости от успеха сопоставления
предыдущего подшаблона. У условных подшаблонов две допустимые формы:
При успешном сопоставлении условия condition будет выбран
подшаблон yes-pattern, иначе — no-pattern, если он есть.
Если указали более двух альтернатив, парсер выдаст ошибку времени компиляции.
Условия бывают двух видов. Если между скобками
заключены цифры, условие будет выполняться в том случае,
если подшаблон с соответствующим номером был успешно сопоставлен.
Рассмотрим следующий шаблон (он содержит незначащий пробел для удобства
чтения, подразумевается использование модификатора
PCRE_EXTENDED),
разделив его для удобства на три смысловые части:
Первая часть соответствует опциональной открывающей скобке,
и в случае если она присутствует, захватывает её как значение
первого подшаблона. Следующая часть соответствует одному или более
символам, которые отличаются от круглой скобки. Третья часть — условный
подшаблон, который зависит от результата сопоставления первого подшаблона.
Если в начале обрабатываемых данных парсер обнаружил
открывающую круглую скобку, условие будет интерпретировано как
истина, и, следовательно, для успешного сопоставления третьей
части шаблона необходима закрывающая круглая скобка. В противном случае,
поскольку вторую ветвь условного шаблона не указали,
парсер сопоставит третью часть с пустой строкой. Суммируя все вышесказанное,
приведённый шаблон совпадает с последовательностью не-скобок,
возможно, заключённой в круглые скобки.
Если условие — строка (R), парсер выполнит
условие, если будет произведён рекурсивный вызов к шаблону или
подшаблону. На «самом верхнем уровне» условие ложно.
Если условие не является последовательностью цифр или (R),
оно должно быть утверждением. Это может быть либо положительная
или отрицательная проверка последующего либо предыдущего текста.
Рассмотрим данный шаблон, снова содержащий незначащие пробелы,
с двумя альтернативами на второй строке:
Приведён пример с утверждающим условием касательно предшествующего
текста, которое выполняется для необязательной последовательности
не-букв с последующей буквой. Говоря другими словами, указанное
условие проверяет наличие хотя бы одной предшествующей буквы.
Если буква найдена, выполняется сопоставление с первой
альтернативой, в противном случае — со второй альтернативой.
Приведённый шаблон соответствует строкам двух видов:
dd-aaa-dd либо dd-dd-dd, где aaaa — это буквы, а dd — цифры.
Комментарии
Служебная последовательность (?# обозначает начало комментария,
который продолжается до ближайшей закрывающей скобки. Вложенные
скобки не допускаются. Символы, находящиеся внутри комментария,
не принимают участия в сопоставлении шаблона.
Если используется модификатор
PCRE_EXTENDED,
неэкранированный символ «#» вне символьного класса также означает
начало блока комментария, который длится до конца текущей строки.
Использование комментариев в шаблоне PCRE
&example.outputs;
Рекурсивные шаблоны
Рассмотрим проблему сопоставления строки в круглых скобках, которая допускает
неограниченное количество вложенных круглых скобок.
Лучшее, что можно сделать без рекурсии, — написать шаблон, который
будет решать задачу для ограниченной глубины вложенности, поскольку обработать
неограниченную глубину невозможно.
Perl 5.6 предоставил ряд экспериментальных возможностей,
которые в том числе разрешают реализовать рекурсию в шаблонах.
Специальное обозначение (?R) задают, чтобы указать рекурсивный
подшаблон. Таким образом, приведём PCRE-шаблон, который решает поставленную
задачу (подразумевается, что включён модификатор
PCRE_EXTENDED,
незначащие пробелы игнорируются):
\( ( (?>[^()]+) | (?R) )* \)
Вначале шаблон соответствует открывающей круглой скобке. Затем
шаблон соответствует любому количеству подстрок, каждая из которых
может быть последовательностью символов, кроме скобок, либо строкой, рекурсивно
соответствующей шаблону (т. е. строкой, корректно заключённой в
круглые скобки). И, в конце, идёт закрывающая круглая скобка.
Приведённый пример шаблона включает вложенные неограниченные повторения,
поэтому однократные шаблоны значительно ускоряют процесс
сопоставления, особенно когда строка не соответствует заданной
маске. Например, если применить этот шаблон к строке:
(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(),
то несоответствие будет обнаружено достаточно быстро. Но без
однократных шаблонов сопоставление будет затягиваться
на длительное время, поскольку существует множество способов разделения
строки между квантификаторами + и *, и все они должны быть проверены,
прежде чем будет выдано сообщение о неудаче.
Значение, которое устанавливают для захватывающего подшаблона, будет соответствовать
значению, захваченному на наиболее глубоком уровне рекурсии. Когда приведённый шаблон
сопоставляется со строкой
(ab(cd)ef),
захваченным значением будет «ef» — последнее значение,
принимаемое на верхнем уровне. Если добавить дополнительные скобки
\( ( ( (?>[^()]+) | (?R) )* ) \),
захваченным значением будет «ab(cd)ef». Если
в шаблоне встречается более 15 захватывающих скобок, модулю PCRE
требуется больше памяти для обработки рекурсии, чем обычно.
Память выделяется функцией pcre_malloc и освобождается
функцией pcre_free. Если функция не может выделить память,
сохраняются данные только для первых 15 захватывающих скобок,
поскольку нет способа выдать ошибку out-of-memory изнутри рекурсии.
Конструкции (?1), (?2) и так далее
указывают для рекурсивных подшаблонов.
Допустимо также указывать именованные подшаблоны:
(?P>name) или (?&name).
Если синтаксис ссылки на рекурсивный подшаблон (как по имени, так и по
числовому индексу) указали вне скобок, к которым он относится,
парсер обработает подшаблон аналогично подпрограмме в языке программирования.
Возьмём более ранний пример, который указывает, что шаблон
(sens|respons)e and \1ibility
соответствует значениям «sense and sensibility» и «response and responsibility»,
но не «sense and responsibility». Если вместо этого написать шаблон
(sens|respons)e and (?1)ibility,
он совпадёт со значением «sense and responsibility» так же, как и с другими двумя
строками. Однако такие ссылки должны быть указаны после подшаблона, на который
они ссылаются.
Максимальная длина входной строки — наибольшее положительное число,
которое может содержать целочисленная переменная. Однако, поскольку для обработки
подшаблонов и бесконечного повторения модуль PCRE запускает рекурсию, это означает,
что размер обрабатываемых строк в некоторых шаблонах также может быть
ограничен доступным размером стека.
Производительность
Некоторые элементы, которые встречаются в шаблонах, более
эффективны, чем другие. Например, символьный класс [aeiou]
работает гораздо эффективнее набора альтернатив (a|e|i|o|u).
Как правило, более простая конструкция наболее эффективна.
Книга Джеффри Фридла содержит много обсуждений вопроса оптимизации
регулярных выражений.
Если шаблон начинается с .* и включили флаг
PCRE_DOTALL,
шаблон неявно заякоривается, поскольку шаблон может совпадать только
в начале строки. Но если модификатор
PCRE_DOTALL
не включили, PCRE не может выполнить соответствующую оптимизацию,
поскольку в этой ситуации метасимвол «.» не соответствует символу начала
строки (если обрабатываемые данные содержат переводы строк, такой шаблон
может соответствовать шаблону не от начала строки, а от позиции
непосредственно после перевода строки).
Например, применяя шаблон
(.*) second
к строке «first\nand second» (где \n — символ перевода строки), значение,
которое захватил первый подшаблон, будет «and».
Чтобы обработать все возможные точки соответствия, модуль PCRE пытается
сопоставить шаблон после каждого символа перевода строки.
При работе с подобными шаблонами и обработке
данных без переводов строк для лучшей производительности
включают модификатор
PCRE_DOTALL,
либо начинают шаблон с циркумфлекса ^.* для явного заякоривания.
Это защитит модуль PCRE от поиска символов новых строк и дополнительных
попыток сопоставить шаблон с каждой такой найденной позицией.
Избегайте шаблонов, которые содержат вложенные неограниченные повторения.
Сопоставление таких шаблонов со строками, которые не содержат совпадений,
занимает длительное время. Рассмотрим пример шаблона
(a+)*
Он может соответствовать значению «aaaa» тридцатью тремя различными способами,
и эта цифра очень быстро растёт при увеличении строки. (В примере
квантификатор * может совпадать 0, 1, 2, 3 или 4 раза,
и для каждого такого случая, кроме нуля, квантификатор + также может
совпадать различное число раз.) Если остаток шаблона таков, что все
совпадение терпит неудачу, модуль PCRE должен попробовать все возможные
варианты совпадения, что может потребовать огромного количества времени.
За счёт оптимизации отлавливают наиболее простые случаи наподобие
(a+)*b
где следом идёт литеральный символ. Перед началом стандартной
процедуры поиска модуль PCRE проверяет в следующей подстроке наличие
символа «b», и если символа нет, попытка сопоставления
немедленно завершается неудачей. Однако, когда последующего литерала нет,
оптимизация не может быть применена. Разница заметна при сравнении поведения шаблона
(a+)*\d
с поведением приведённого выше шаблона. Первый определяет
невозможность сопоставления практически сразу, при сопоставлении
со строкой, которая состоит из символов «a», тогда как второй
тратит длительное время на поиск в строках с длиной больше 20 символов.