1
0
mirror of https://github.com/php/doc-ru.git synced 2026-03-23 23:32:16 +01:00
Files
archived-doc-ru/reference/pcre/pattern.syntax.xml
2026-02-11 06:44:16 +03:00

2386 lines
142 KiB
XML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?xml version="1.0" encoding="utf-8"?>
<!-- EN-Revision: a0434e05111acabf2b9b2c7847e7e733f8dab0dc Maintainer: shein Status: ready -->
<!-- Reviewed: no -->
<chapter xml:id="reference.pcre.pattern.syntax" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Описание синтаксиса Perl-совместимых регулярных выражений</title>
<titleabbrev>Синтаксис регулярных выражений</titleabbrev>
<section xml:id="regexp.introduction">
<title>Вступление</title>
<para>
Раздел описывает синтаксис и семантику регулярных выражений, которые поддерживает
модуль PCRE. Регулярные выражения также описывают документация
языка Perl и много других книг, отдельные содержат большое количество
примеров. Подробно регулярные выражения описывает книга
Джеффри Фридла «Регулярные выражения» (Jeffrey Friedl
“Mastering Regular Expressions”), опубликованная издательством O'Reilly
(ISBN 1-56592-257-3).
Описание здесь привели как справочную информацию.
</para>
<para>
Регулярное выражение — это шаблон, с которым входная строка сравнивается слева направо.
Большинство символов в шаблоне — буквально сами символы,
которые совпадают с символами во входной строке.
В качестве банального примера, шаблон
<literal>The quick brown fox</literal>
соответствует той части искомой строки, которая с ним совпадает.
</para>
</section>
<section xml:id="regexp.reference.delimiters">
<title>Разделители</title>
<para>
При работе с функциями модуля PCRE шаблон заключают
в <emphasis>разделители</emphasis>.
В качестве разделителя разрешены любые символы, кроме буквенно-цифровых, обратного
слеша или пробельных символов.
Парсер без предупреждения проигнорирует пробел перед корректным разделителем.
</para>
<para>
Разделители, которыми пользуются чаще: слеши (<literal>/</literal>),
знаки решётки (<literal>#</literal>) и тильды (<literal>~</literal>).
Следующий абзац приводит примеры шаблонов с корректными разделителями.
<informalexample>
<programlisting>
<![CDATA[
/foo bar/
#^[^0-9]$#
+php+
%[a-zA-Z0-9_-]%
]]>
</programlisting>
</informalexample>
</para>
<para>
Скобки также указывают как разделители, где начальный разделитель —
открывающая скобка, а конечный разделитель —
закрывающая. Символы <literal>()</literal>, <literal>{}</literal>,
<literal>[]</literal> и <literal>&lt;&gt;</literal>
допустимые пары разделителей.
<informalexample>
<programlisting>
<![CDATA[
(this [is] a (pattern))
{this [is] a (pattern)}
[this [is] a (pattern)]
<this [is] a (pattern)>
]]>
</programlisting>
</informalexample>
Разделители в виде скобок не нужно экранировать, если скобки также задали
как метасимволы в шаблоне, но как и с другими разделителями скобки нужно
экранировать, если их указывают непосредственно как символы.
</para>
<para>
Разделитель экранируют обратным слешем «\», если нужно указать разделитель
внутри шаблона. Если разделитель часто указывают в шаблоне, в целях удобочитаемости
выбирают другой разделитель для этого шаблона.
<informalexample>
<programlisting>
<![CDATA[
/http:\/\//
#http://#
]]>
</programlisting>
</informalexample>
Функцией <function>preg_quote</function> пользуются, чтобы
экранировать строку в шаблоне, а необязательный второй параметр функции
разрешает задать разделитель.
</para>
<para>
После закрывающего разделителя указывают
<link linkend="reference.pcre.pattern.modifiers">модификаторы
шаблонов</link>. Следующий пример показывает сопоставление без учёта регистра:
<informalexample>
<programlisting>
<![CDATA[
#[a-z]#i
]]>
</programlisting>
</informalexample>
</para>
</section>
<section xml:id="regexp.reference.meta">
<title>Метасимволы</title>
<para>
Сила регулярных выражений состоит в том, что в шаблон можно включать подшаблоны
для альтернативного сопоставления и поиска повторений.
Альтернативы и повторения кодируют в шаблоне <emphasis>метасимволами</emphasis>,
которые парсер интерпретирует не как самих себя, а особым образом.
</para>
<para>
В коде метасимволы записывают двумя способами: одни парсер распознаёт за пределами
квадратных скобок, другие — внутри квадратных скобок. Вне квадратных скобок
распознаются следующие метасимволы:
<table>
<title>Метасимволы вне квадратных скобок</title>
<tgroup cols="2">
<thead>
<row>
<entry>Метасимвол</entry>
<entry>Описание</entry>
</row>
</thead>
<tbody>
<row>
<entry>\</entry>
<entry>общий экранирующий символ с несколькими назначениями</entry>
</row>
<row>
<entry>^</entry>
<entry>декларирует начало данных (или строки в многострочном режиме)</entry>
</row>
<row>
<entry>$</entry>
<entry>декларирует конец данных или до завершения строки (или окончание строки в многострочном режиме)</entry>
</row>
<row>
<entry>.</entry>
<entry>соответствует любому символу, кроме перевода строки (по умолчанию)</entry>
</row>
<row>
<entry>[</entry>
<entry>начало описания символьного класса</entry>
</row>
<row>
<entry>]</entry>
<entry>конец описания символьного класса</entry>
</row>
<row>
<entry>|</entry>
<entry>начало ветки условного выбора</entry>
</row>
<row>
<entry>(</entry>
<entry>начало подшаблона</entry>
</row>
<row>
<entry>)</entry>
<entry>конец подшаблона</entry>
</row>
<row>
<entry>?</entry>
<entry>
расширяет смысл метасимвола <literal>(</literal>,
выступает в роли квантификатора, который означает 0 или 1 вхождение,
а также преобразовывает жадные квантификаторы в ленивые
(смотрите раздел «<link linkend="regexp.reference.repetition">Повторение</link>»)
</entry>
</row>
<row>
<entry>*</entry>
<entry>квантификатор, который означает 0 или более вхождений</entry>
</row>
<row>
<entry>+</entry>
<entry>квантификатор, который означает 1 или более вхождений</entry>
</row>
<row>
<entry>{</entry>
<entry>начало количественного квантификатора</entry>
</row>
<row>
<entry>}</entry>
<entry>конец количественного квантификатора</entry>
</row>
</tbody>
</tgroup>
</table>
Часть шаблона в квадратных скобках называется
<link linkend="regexp.reference.character-classes">символьным классом</link>.
В символьный класс входят только следующие метасимволы:
<table>
<title>
Метасимволы внутри квадратных скобок (<emphasis>символьные классы</emphasis>)
</title>
<tgroup cols="2">
<thead>
<row>
<entry>Метасимвол</entry>
<entry>Описание</entry>
</row>
</thead>
<tbody>
<row>
<entry>\</entry>
<entry>общий экранирующий символ</entry>
</row>
<row>
<entry>^</entry>
<entry>означает отрицание класса, допустим только в начале класса</entry>
</row>
<row>
<entry>-</entry>
<entry>означает символьный интервал</entry>
</row>
</tbody>
</tgroup>
</table>
Другие разделы детально описывают каждый из перечисленных метасимволов.
</para>
</section>
<section xml:id="regexp.reference.escape">
<title>Экранирующие последовательности</title>
<para>
У обратного слеша «\» несколько назначений. Главное — если обратный слеш
предшествует небуквенно-цифровому символу, обратный слеш снимает с этого символа
специальное значение, которое символ мог иметь. Обратный слеш как экранирующий символ
допустим как в символьном классе, так и вне него.
</para>
<para>
Например, для сопоставления символа «*» в шаблоне указывают «\*».
Такой подход применяют независимо от того,
интерпретировал бы парсер символ, который идёт за обратным слешем, как
специальный без экранирования или нет. Поэтому небуквенно-цифровые символы
безопасно экранировать символом обратного слеша «\», чтобы указать,
что символ означает сам себя. Для сопоставления
обратного слеша с самим собой, записывают «\\».
</para>
<note>
<para>
Обратный слеш в одинарных и двойных <link linkend="language.types.string.syntax">PHP-строках</link>
работает как служебный символ. Поэтому когда нужно сопоставить символ \ с регулярным выражением \\,
в PHP-коде указывают "\\\\" или '\\\\'.
</para>
</note>
<para>
Если указали модификатор
<link linkend="reference.pcre.pattern.modifiers">PCRE_EXTENDED</link>,
пробельные символы в шаблоне вне описания символьного класса парсер проигнорирует.
Также игнорируется часть строки, которая находится между символом «#»,
который, опять же, не участвует в описании символьного класса,
и следующим символом перевода строки. В этой ситуации обратный слеш указывают
как экранирующий символ для обозначения вхождений пробельных символов
или символа «#» в шаблоне.
</para>
<para>
Второе назначение обратного слеша — кодировать непечатаемые символы в шаблоне в видимой форме.
Нет ограничений на появление непечатаемых символов, кроме двоичного нуля,
который завершает шаблон, но при подготовке шаблона в текстовом редакторе
проще записывать следующие экранирующие последовательности, чем двоичный символ,
который представляет такую последовательность:
</para>
<para>
<variablelist>
<varlistentry>
<term><emphasis>\a</emphasis></term>
<listitem>
<simpara>символ оповещения, сигнал, (BEL, шестнадцатеричный код 07)</simpara>
</listitem>
</varlistentry>
<varlistentry>
<term><emphasis>\cx</emphasis></term>
<listitem>
<simpara>«control + x», где x — произвольный символ</simpara>
</listitem>
</varlistentry>
<varlistentry>
<term><emphasis>\e</emphasis></term>
<listitem>
<simpara>escape (шестнадцатеричный код 1B)</simpara>
</listitem>
</varlistentry>
<varlistentry>
<term><emphasis>\f</emphasis></term>
<listitem>
<simpara>разрыв страницы (шестнадцатеричный код 0C)</simpara>
</listitem>
</varlistentry>
<varlistentry>
<term><emphasis>\n</emphasis></term>
<listitem>
<simpara>перевод строки (шестнадцатеричный код 0A)</simpara>
</listitem>
</varlistentry>
<varlistentry>
<term><emphasis>\p{xx}</emphasis></term>
<listitem>
<simpara>
символ со свойством xx, подробнее об этом рассказывает раздел
«<link linkend="regexp.reference.unicode">Свойства Unicode-символов</link>»
</simpara>
</listitem>
</varlistentry>
<varlistentry>
<term><emphasis>\P{xx}</emphasis></term>
<listitem>
<simpara>
символ без свойства xx, подробнее об этом рассказывает раздел
«<link linkend="regexp.reference.unicode">Свойства Unicode-символов</link>»
</simpara>
</listitem>
</varlistentry>
<varlistentry>
<term><emphasis>\r</emphasis></term>
<listitem>
<simpara>возврат каретки (шестнадцатеричный код 0D)</simpara>
</listitem>
</varlistentry>
<varlistentry>
<term><emphasis>\R</emphasis></term>
<listitem>
<simpara>разрыв строки: совпадает с \n, \r и \r\n</simpara>
</listitem>
</varlistentry>
<varlistentry>
<term><emphasis>\t</emphasis></term>
<listitem>
<simpara>табуляция (шестнадцатеричный код 09)</simpara>
</listitem>
</varlistentry>
<varlistentry>
<term><emphasis>\xhh</emphasis></term>
<listitem>
<simpara>
символ с шестнадцатеричным кодом hh
</simpara>
</listitem>
</varlistentry>
<varlistentry>
<term><emphasis>\ddd</emphasis></term>
<listitem>
<simpara>символ с восьмеричным кодом ddd, либо ссылка на подшаблон</simpara>
</listitem>
</varlistentry>
</variablelist>
</para>
<para>
Если быть более точным, комбинация «<literal>\cx</literal>» интерпретируется
следующим образом: если «<literal>x</literal>» — символ нижнего регистра,
он преобразовывается в верхний регистр.
После этого шестой бит символа (шестнадцатеричный код 40) инвертируется.
Таким образом, комбинация «<literal>\cz</literal>» интерпретируется как шестнадцатеричное
значение 1A, тогда как комбинация «<literal>\c{</literal>» получает шестнадцатеричное
значение 3B, а «<literal>\c;</literal>» — 7B.
</para>
<para>
После комбинации «<literal>\x</literal>» считываются ещё две шестнадцатеричные цифры
в нижнем или верхнем регистре. В <emphasis>режиме UTF-8</emphasis> разрешена запись
«<literal>\x{...}</literal>», где содержимое скобок — строка из шестнадцатеричных цифр.
Такая запись интерпретируется как символ UTF-8, кодовый номер которого — шестнадцатеричное число.
Исходная шестнадцатеричная экранирующая последовательность <literal>\xhh</literal>
совпадает с двухбайтовым символом в кодировке UTF-8, если значение шестнадцатеричного числа
превышает 127.
</para>
<para>
После комбинации «<literal>\0</literal>» считываются ещё две восьмеричные цифры.
Если в записи менее двух цифр, считываются только те, которые указали.
Таким образом, последовательность «<literal>\0\x\07</literal>» будет интерпретирована
как два двоичных нуля, за которыми следует символ оповещения (звонок). Убедитесь, что
указали две цифры после начального нуля, если символ, который идёт за нулём, сам
восьмеричная цифра.
</para>
<para>
Обработка обратного слеша, за которым следует ненулевая цифра, несколько сложнее.
Вне символьного класса модуль PCRE воспринимает обратный слеш и цифры за ним
как десятичное число. Если полученное значение меньше десяти
или если шаблон содержит по меньшей мере такое же количество предшествующих
текущей позиции подшаблонов, парсер интерпретирует всю конструкцию как
<emphasis>ссылку на подшаблон</emphasis>.
Более детальное описание приведём ниже при обсуждении механизма работы подшаблонов.
</para>
<para>
Внутри символьного класса, либо если полученное значение больше 9 и соответствующее
количество предшествующих подшаблонов отсутствует, модуль PCRE считывает до трёх восьмеричных
цифр, которые следуют за обратным слешем, и генерирует один байт из последних 8-ми
значащих битов полученного значения. Все последующие цифры обозначают сами себя. Например:
</para>
<para>
<variablelist>
<varlistentry>
<term><emphasis>\040</emphasis></term>
<listitem><simpara>ещё один способ записи пробела</simpara></listitem>
</varlistentry>
<varlistentry>
<term><emphasis>\40</emphasis></term>
<listitem>
<simpara>
то же самое, если данной записи предшествует менее сорока подшаблонов
</simpara>
</listitem>
</varlistentry>
<varlistentry>
<term><emphasis>\7</emphasis></term>
<listitem><simpara>всегда интерпретируется как ссылка на подшаблон</simpara></listitem>
</varlistentry>
<varlistentry>
<term><emphasis>\11</emphasis></term>
<listitem>
<simpara>
может быть как обратной ссылкой, так и альтернативной записью символа табуляции
</simpara>
</listitem>
</varlistentry>
<varlistentry>
<term><emphasis>\011</emphasis></term>
<listitem><simpara>всегда интерпретируется как символ табуляции</simpara></listitem>
</varlistentry>
<varlistentry>
<term><emphasis>\0113</emphasis></term>
<listitem><simpara>символ табуляции, за которым следует цифра «3»</simpara></listitem>
</varlistentry>
<varlistentry>
<term><emphasis>\113</emphasis></term>
<listitem>
<simpara>
интерпретируется как символ с восьмеричным кодом 113 (так как ссылок на подшаблоны не может быть больше 99)
</simpara>
</listitem>
</varlistentry>
<varlistentry>
<term><emphasis>\377</emphasis></term>
<listitem><simpara>байт, всецело состоящий из единичных битов</simpara></listitem>
</varlistentry>
<varlistentry>
<term><emphasis>\81</emphasis></term>
<listitem>
<simpara>
либо обратная ссылка, либо бинарный ноль, за которым следуют цифры «8» и «1»
</simpara>
</listitem>
</varlistentry>
</variablelist>
</para>
<para>
Следует помнить, что восьмеричные значения, превышающие 100, следует писать без
лидирующего нуля, так как читается не более трёх восьмеричных цифр.
</para>
<para>
Все последовательности, определяющие однобайтное значение, могут встречаться
как внутри, так и вне символьных классов. Кроме того, внутри символьного класса
запись «<literal>\b</literal>» интерпретируется как символ возврата
(«backspace», шестнадцатеричный код 08). Вне символьного класса она имеет
другое значение (какое именно, описано ниже).
</para>
<para>
Третье использование обратного слеша — указание общего типа символов:
</para>
<para>
<variablelist>
<varlistentry>
<term><emphasis>\d</emphasis></term>
<listitem><simpara>любая десятичная цифра</simpara></listitem>
</varlistentry>
<varlistentry>
<term><emphasis>\D</emphasis></term>
<listitem><simpara>любой символ, кроме десятичной цифры</simpara></listitem>
</varlistentry>
<varlistentry>
<term><emphasis>\h</emphasis></term>
<listitem><simpara>любой горизонтальный пробельный символ</simpara></listitem>
</varlistentry>
<varlistentry>
<term><emphasis>\H</emphasis></term>
<listitem><simpara>любой символ, не являющийся горизонтальным пробельным символом</simpara></listitem>
</varlistentry>
<varlistentry>
<term><emphasis>\s</emphasis></term>
<listitem><simpara>любой пробельный символ</simpara></listitem>
</varlistentry>
<varlistentry>
<term><emphasis>\S</emphasis></term>
<listitem><simpara>любой непробельный символ</simpara></listitem>
</varlistentry>
<varlistentry>
<term><emphasis>\v</emphasis></term>
<listitem><simpara>любой вертикальный пробельный символ</simpara></listitem>
</varlistentry>
<varlistentry>
<term><emphasis>\V</emphasis></term>
<listitem><simpara>любой символ, не являющийся вертикальным пробельным символом</simpara></listitem>
</varlistentry>
<varlistentry>
<term><emphasis>\w</emphasis></term>
<listitem><simpara>Любой символ, образующий «слово»</simpara></listitem>
</varlistentry>
<varlistentry>
<term><emphasis>\W</emphasis></term>
<listitem><simpara>Любой символ, не образующий «слово»</simpara></listitem>
</varlistentry>
</variablelist>
</para>
<para>
Каждая пара таких специальных последовательностей делит полное множество
всех символов на два непересекающихся множества.
Любой символ соответствует одному и только одному множеству из пары.
</para>
<para>
Следующие символы считаются «пробельными»: HT (9), LF (10), FF (12), CR (13)
и пробел (32). Однако, если идёт сопоставление с учётом локали, символы
с кодовыми точками в диапазоне 128-255 также рассматриваются как пробельные,
например, символ неразрывного пробела NBSP (A0).
</para>
<para>
Символ, образующий «слово» — это произвольная цифра, буква или символ подчёркивания,
проще говоря, любой символ, которому разрешено быть частью
«<emphasis>слова</emphasis>» в Perl. Определение букв и цифр управляется
символьными таблицами, с которыми собрали модуль PCRE. И, как следствие, эти наборы
могут отличаться в различных локализированных дистрибутивах.
Например, в локали «fr» (Франция) ряд символов с кодом выше 128 применяют
для записи ударных символов и поэтому они соответствуют маске <literal>\w</literal>.
</para>
<para>
Описанные типы символов применяют как внутри, так и вне символьных
классов, и типы соответствуют одному символу данного типа. Если текущая точка
сравнения находится в конце строки, ни один из них не сможет совпасть, поскольку
нет символа, с которым могло бы произойти совпадение.
</para>
<para>
Четвёртое назначение обратного слеша — определение некоторых формальных утверждений,
которые описывают условия месторасположения особых позиций в строке
и не затрагивают сами символы. Работу с подшаблонами как с более
сложными формальными утверждениями, документация описывает ниже.
К такими управляющим последовательностям относятся:
</para>
<para>
<variablelist>
<varlistentry>
<term><emphasis>\b</emphasis></term>
<listitem><simpara>граница слова</simpara></listitem>
</varlistentry>
<varlistentry>
<term><emphasis>\B</emphasis></term>
<listitem><simpara>не является границей слова</simpara></listitem>
</varlistentry>
<varlistentry>
<term><emphasis>\A</emphasis></term>
<listitem><simpara>начало данных (независимо от многострочного режима)</simpara></listitem>
</varlistentry>
<varlistentry>
<term><emphasis>\Z</emphasis></term>
<listitem>
<simpara>
конец данных либо позиция перед последним переводом строки (независимо от многострочного режима)
</simpara>
</listitem>
</varlistentry>
<varlistentry>
<term><emphasis>\z</emphasis></term>
<listitem><simpara>конец данных (независимо от многострочного режима)</simpara></listitem>
</varlistentry>
<varlistentry>
<term><emphasis>\G</emphasis></term>
<listitem><simpara>первая совпадающая позиция в строке</simpara></listitem>
</varlistentry>
</variablelist>
</para>
<para>
Описанные выше последовательности не могут встречаться в символьных классах
(исключая комбинацию «<literal>\b</literal>», которая внутри класса означает
символ возврата «backspace»).
</para>
<para>
Границей слова считается такая позиция в строке, в которой из текущего и
предыдущего символа только один соответствует <literal>\w</literal> или
<literal>\W</literal> (т.е. один из них соответствует <literal>\w</literal>,
а другой <literal>\W</literal>). Начало или конец строки также соответствуют
границе слова Если первый или, соответственно, последний символ совпадает
с <literal>\w</literal>.
</para>
<para>
Утверждения <literal>\A</literal>, <literal>\Z</literal> и <literal>\z</literal>
отличаются от традиционных метасимволов начала и конца строки строки —
циркумфлекса ^ и знака доллара $, которые описывает раздел
«<link linkend="regexp.reference.anchors">Якоря</link>»,
тем, что они всегда совпадают только в самом начале и конце входной строки,
какие бы параметры ни установили.
На них никак не влияют опции
<link linkend="reference.pcre.pattern.modifiers">PCRE_MULTILINE</link> и
<link linkend="reference.pcre.pattern.modifiers">PCRE_DOLLAR_ENDONLY</link>.
Разница между <literal>\Z</literal> и <literal>\z</literal> в том,
что <literal>\Z</literal> соответствует позиции перед последним символом,
если последний символ — перевод строки, кроме самого конца строки.
Тогда как <literal>\z</literal> соответствует исключительно концу данных.
</para>
<para>
Утверждение <literal>\G</literal> будет истинным, только если
текущая проверяемая позиция находится в начале совпадения, которое указали
в параметре <parameter>offset</parameter> функции <function>preg_match</function>.
Она отличается от последовательности <literal>\A</literal> при ненулевом значении
параметра <parameter>offset</parameter>.
</para>
<para>
Последовательности <literal>\Q</literal> и <literal>\E</literal>
игнорируют метасимволы регулярного выражения в шаблоне. Например:
шаблон <literal>\w+\Q.$.\E$</literal> совпадёт с «заякоренными»
в конце строки одним или более символами слова, за которыми следуют
литералы <literal>.$.</literal>. Обратите внимание, что это не меняет поведение
разделителей; например, шаблон <literal>#\Q#\E#$</literal>
неправильный, потому что второй символ <literal>#</literal> отмечает конец шаблона,
а последовательность <literal>\E#</literal> парсер интерпретирует как недопустимые модификаторы.
</para>
<para>
Последовательность <literal>\K</literal> сбрасывает начало совпадения. Например,
шаблон <literal>foo\Kbar</literal> совпадёт со значением «foobar», но сообщит,
что совпал только с «bar». Последовательность
<literal>\K</literal> не мешает установке подшаблонов. Например, если шаблон
<literal>(foo)\Kbar</literal> совпадёт со строкой «foobar», первым подшаблоном
всё равно будет значение «foo».
</para>
</section>
<section xml:id="regexp.reference.unicode">
<title>Свойства Unicode-символов</title>
<para>
С версии стандарта Unicode 5.1.0 при выборе <emphasis>режима UTF-8</emphasis>
доступны три дополнительные управляющие последовательности для соответствия
общим типам символов. Вот они:
</para>
<variablelist>
<varlistentry>
<term><emphasis>\p{xx}</emphasis></term>
<listitem><simpara>символ со свойством xx</simpara></listitem>
</varlistentry>
<varlistentry>
<term><emphasis>\P{xx}</emphasis></term>
<listitem><simpara>символ без свойства xx</simpara></listitem>
</varlistentry>
<varlistentry>
<term><emphasis>\X</emphasis></term>
<listitem><simpara>расширенная последовательность Unicode</simpara></listitem>
</varlistentry>
</variablelist>
<para>
Имена свойств, которые выше представили символами <literal>xx</literal>, ограничены
общими категориями свойств Unicode. У каждого символа есть ровно одно такое свойство,
которое обозначено двухбуквенной аббревиатурой. Для совместимости с Perl отрицание
можно указать, включив символ циркумфлекса «^» между открывающей скобкой и именем свойства.
Например, <literal>\p{^Lu}</literal> — это то же самое, что и <literal>\P{Lu}</literal>.
</para>
<para>
Если с управляющей последовательностью <literal>\p</literal>
или <literal>\P</literal> указали только одну букву,
она включает все свойства, которые начинаются с этой буквы.
В этом случае при отсутствии отрицания фигурные скобки необязательны;
следующие два примера эквивалентны:
</para>
<informalexample>
<programlisting>
<![CDATA[
\p{L}
\pL
]]>
</programlisting>
</informalexample>
<table>
<title>Поддерживаемые коды свойств</title>
<tgroup cols="3">
<thead>
<row>
<entry>Свойство</entry>
<entry>Совпадение</entry>
<entry>Замечание</entry>
</row>
</thead>
<tbody>
<row>
<entry><literal>C</literal></entry>
<entry>Другое</entry>
<entry></entry>
</row>
<row>
<entry><literal>Cc</literal></entry>
<entry>Control</entry>
<entry></entry>
</row>
<row>
<entry><literal>Cf</literal></entry>
<entry>Формат</entry>
<entry></entry>
</row>
<row>
<entry><literal>Cn</literal></entry>
<entry>Не присвоено</entry>
<entry></entry>
</row>
<row>
<entry><literal>Co</literal></entry>
<entry>Частное использование</entry>
<entry></entry>
</row>
<row rowsep="1">
<entry><literal>Cs</literal></entry>
<entry>Суррогат</entry>
<entry></entry>
</row>
<row>
<entry><literal>L</literal></entry>
<entry>Буква</entry>
<entry>
Включает следующие свойства: <literal>Ll</literal>,
<literal>Lm</literal>, <literal>Lo</literal>, <literal>Lt</literal> и
<literal>Lu</literal>.
</entry>
</row>
<row>
<entry><literal>Ll</literal></entry>
<entry>Строчная буква</entry>
<entry></entry>
</row>
<row>
<entry><literal>Lm</literal></entry>
<entry>Модификатор буквы</entry>
<entry></entry>
</row>
<row>
<entry><literal>Lo</literal></entry>
<entry>Другая буква</entry>
<entry></entry>
</row>
<row>
<entry><literal>Lt</literal></entry>
<entry>Заглавная буква</entry>
<entry></entry>
</row>
<row rowsep="1">
<entry><literal>Lu</literal></entry>
<entry>Прописная буква</entry>
<entry></entry>
</row>
<row>
<entry><literal>M</literal></entry>
<entry>Знак</entry>
<entry></entry>
</row>
<row>
<entry><literal>Mc</literal></entry>
<entry>Пробельный знак</entry>
<entry></entry>
</row>
<row>
<entry><literal>Me</literal></entry>
<entry>Окружающий знак</entry>
<entry></entry>
</row>
<row rowsep="1">
<entry><literal>Mn</literal></entry>
<entry>Не пробельный знак</entry>
<entry></entry>
</row>
<row>
<entry><literal>N</literal></entry>
<entry>Число</entry>
<entry></entry>
</row>
<row>
<entry><literal>Nd</literal></entry>
<entry>Десятичное число</entry>
<entry></entry>
</row>
<row>
<entry><literal>Nl</literal></entry>
<entry>Буквенное число</entry>
<entry></entry>
</row>
<row rowsep="1">
<entry><literal>No</literal></entry>
<entry>Другое число</entry>
<entry></entry>
</row>
<row>
<entry><literal>P</literal></entry>
<entry>Пунктуация</entry>
<entry></entry>
</row>
<row>
<entry><literal>Pc</literal></entry>
<entry>Соединяющая пунктуация</entry>
<entry></entry>
</row>
<row>
<entry><literal>Pd</literal></entry>
<entry>Знаки тире</entry>
<entry></entry>
</row>
<row>
<entry><literal>Pe</literal></entry>
<entry>Закрывающая пунктуация</entry>
<entry></entry>
</row>
<row>
<entry><literal>Pf</literal></entry>
<entry>Заключительная пунктуация</entry>
<entry></entry>
</row>
<row>
<entry><literal>Pi</literal></entry>
<entry>Начальная пунктуация</entry>
<entry></entry>
</row>
<row>
<entry><literal>Po</literal></entry>
<entry>Другая пунктуация</entry>
<entry></entry>
</row>
<row rowsep="1">
<entry><literal>Ps</literal></entry>
<entry>Открывающая пунктуация</entry>
<entry></entry>
</row>
<row>
<entry><literal>S</literal></entry>
<entry>Символ</entry>
<entry></entry>
</row>
<row>
<entry><literal>Sc</literal></entry>
<entry>Денежный знак</entry>
<entry></entry>
</row>
<row>
<entry><literal>Sk</literal></entry>
<entry>Модификатор символа</entry>
<entry></entry>
</row>
<row>
<entry><literal>Sm</literal></entry>
<entry>Математический символ</entry>
<entry></entry>
</row>
<row rowsep="1">
<entry><literal>So</literal></entry>
<entry>Другой символ</entry>
<entry>Включая эмодзи</entry>
</row>
<row>
<entry><literal>Z</literal></entry>
<entry>Разделитель</entry>
<entry></entry>
</row>
<row>
<entry><literal>Zl</literal></entry>
<entry>Разделитель строки</entry>
<entry></entry>
</row>
<row>
<entry><literal>Zp</literal></entry>
<entry>Разделитель абзаца</entry>
<entry></entry>
</row>
<row>
<entry><literal>Zs</literal></entry>
<entry>Пробельный разделитель</entry>
<entry></entry>
</row>
</tbody>
</tgroup>
</table>
<para>
Модуль PCRE не поддерживает расширенные свойства наподобие музыкальных символов
(<literal>InMusicalSymbols</literal>).
</para>
<para>
Указание регистронезависимого (безрегистрового) режима не влияет на эти
управляющие последовательности. Например, запись <literal>\p{Lu}</literal> всегда
совпадает только с прописными буквами.
</para>
<para>
Наборы Unicode-символов описываются как те, что принадлежат конкретным сценариям.
Любой символ из этих наборов сопоставим с помощью имени сценария.
Например:
</para>
<itemizedlist>
<listitem>
<simpara><literal>\p{Greek}</literal></simpara>
</listitem>
<listitem>
<simpara><literal>\P{Han}</literal></simpara>
</listitem>
</itemizedlist>
<para>
Символы, которые не принадлежат конкретному сценарию, объединяются
в сценарий <literal>Common</literal>. Текущий список сценариев:
</para>
<table>
<title>Поддерживаемые сценарии</title>
<tgroup cols="5">
<tbody>
<row>
<entry><literal>Arabic</literal></entry>
<entry><literal>Armenian</literal></entry>
<entry><literal>Avestan</literal></entry>
<entry><literal>Balinese</literal></entry>
<entry><literal>Bamum</literal></entry>
</row>
<row>
<entry><literal>Batak</literal></entry>
<entry><literal>Bengali</literal></entry>
<entry><literal>Bopomofo</literal></entry>
<entry><literal>Brahmi</literal></entry>
<entry><literal>Braille</literal></entry>
</row>
<row>
<entry><literal>Buginese</literal></entry>
<entry><literal>Buhid</literal></entry>
<entry><literal>Canadian_Aboriginal</literal></entry>
<entry><literal>Carian</literal></entry>
<entry><literal>Chakma</literal></entry>
</row>
<row>
<entry><literal>Cham</literal></entry>
<entry><literal>Cherokee</literal></entry>
<entry><literal>Common</literal></entry>
<entry><literal>Coptic</literal></entry>
<entry><literal>Cuneiform</literal></entry>
</row>
<row>
<entry><literal>Cypriot</literal></entry>
<entry><literal>Cyrillic</literal></entry>
<entry><literal>Deseret</literal></entry>
<entry><literal>Devanagari</literal></entry>
<entry><literal>Egyptian_Hieroglyphs</literal></entry>
</row>
<row>
<entry><literal>Ethiopic</literal></entry>
<entry><literal>Georgian</literal></entry>
<entry><literal>Glagolitic</literal></entry>
<entry><literal>Gothic</literal></entry>
<entry><literal>Greek</literal></entry>
</row>
<row>
<entry><literal>Gujarati</literal></entry>
<entry><literal>Gurmukhi</literal></entry>
<entry><literal>Han</literal></entry>
<entry><literal>Hangul</literal></entry>
<entry><literal>Hanunoo</literal></entry>
</row>
<row>
<entry><literal>Hebrew</literal></entry>
<entry><literal>Hiragana</literal></entry>
<entry><literal>Imperial_Aramaic</literal></entry>
<entry><literal>Inherited</literal></entry>
<entry><literal>Inscriptional_Pahlavi</literal></entry>
</row>
<row>
<entry><literal>Inscriptional_Parthian</literal></entry>
<entry><literal>Javanese</literal></entry>
<entry><literal>Kaithi</literal></entry>
<entry><literal>Kannada</literal></entry>
<entry><literal>Katakana</literal></entry>
</row>
<row>
<entry><literal>Kayah_Li</literal></entry>
<entry><literal>Kharoshthi</literal></entry>
<entry><literal>Khmer</literal></entry>
<entry><literal>Lao</literal></entry>
<entry><literal>Latin</literal></entry>
</row>
<row>
<entry><literal>Lepcha</literal></entry>
<entry><literal>Limbu</literal></entry>
<entry><literal>Linear_B</literal></entry>
<entry><literal>Lisu</literal></entry>
<entry><literal>Lycian</literal></entry>
</row>
<row>
<entry><literal>Lydian</literal></entry>
<entry><literal>Malayalam</literal></entry>
<entry><literal>Mandaic</literal></entry>
<entry><literal>Meetei_Mayek</literal></entry>
<entry><literal>Meroitic_Cursive</literal></entry>
</row>
<row>
<entry><literal>Meroitic_Hieroglyphs</literal></entry>
<entry><literal>Miao</literal></entry>
<entry><literal>Mongolian</literal></entry>
<entry><literal>Myanmar</literal></entry>
<entry><literal>New_Tai_Lue</literal></entry>
</row>
<row>
<entry><literal>Nko</literal></entry>
<entry><literal>Ogham</literal></entry>
<entry><literal>Old_Italic</literal></entry>
<entry><literal>Old_Persian</literal></entry>
<entry><literal>Old_South_Arabian</literal></entry>
</row>
<row>
<entry><literal>Old_Turkic</literal></entry>
<entry><literal>Ol_Chiki</literal></entry>
<entry><literal>Oriya</literal></entry>
<entry><literal>Osmanya</literal></entry>
<entry><literal>Phags_Pa</literal></entry>
</row>
<row>
<entry><literal>Phoenician</literal></entry>
<entry><literal>Rejang</literal></entry>
<entry><literal>Runic</literal></entry>
<entry><literal>Samaritan</literal></entry>
<entry><literal>Saurashtra</literal></entry>
</row>
<row>
<entry><literal>Sharada</literal></entry>
<entry><literal>Shavian</literal></entry>
<entry><literal>Sinhala</literal></entry>
<entry><literal>Sora_Sompeng</literal></entry>
<entry><literal>Sundanese</literal></entry>
</row>
<row>
<entry><literal>Syloti_Nagri</literal></entry>
<entry><literal>Syriac</literal></entry>
<entry><literal>Tagalog</literal></entry>
<entry><literal>Tagbanwa</literal></entry>
<entry><literal>Tai_Le</literal></entry>
</row>
<row>
<entry><literal>Tai_Tham</literal></entry>
<entry><literal>Tai_Viet</literal></entry>
<entry><literal>Takri</literal></entry>
<entry><literal>Tamil</literal></entry>
<entry><literal>Telugu</literal></entry>
</row>
<row>
<entry><literal>Thaana</literal></entry>
<entry><literal>Thai</literal></entry>
<entry><literal>Tibetan</literal></entry>
<entry><literal>Tifinagh</literal></entry>
<entry><literal>Ugaritic</literal></entry>
</row>
<row>
<entry><literal>Vai</literal></entry>
<entry><literal>Yi</literal></entry>
<entry />
<entry />
<entry />
<entry />
</row>
</tbody>
</tgroup>
</table>
<para>
Последовательность <literal>\X</literal> соответствует кластеру расширенных Unicode-графем.
Расширенный графемный кластер — один или несколько Unicode-символов,
которые объединяются в один символьный знак (глиф). По сути, его можно рассматривать
как Unicode-эквивалент для <literal>.</literal>, поскольку он находит один независимый
комплексный символ, независимо от того, сколько отдельных символов нужно для его отрисовки.
</para>
<para>
Для версий PCRE до 8.32 (что соответствует версиям PHP до 5.4.14 при работе
со встроенным модулем PCRE), последовательность <literal>\X</literal> равносильна
записи <literal>(?&gt;\PM\pM*)</literal>. Таким образом, он ищет символы без
свойства «mark», и рассматривает последовательность как атомарную группу (см ниже).
Символы со свойством «mark» обычно являются отличительными признаками, которые влияют на предыдущий символ.
</para>
<para>
Совпадение символов по Unicode-свойству — не быстрая операция,
поскольку модулю PCRE приходится выполнить поиск в структуре данных,
которая содержит более пятнадцати тысяч символов. Поэтому традиционные управляющие
последовательности в модуле PCRE, например <literal>\d</literal> и <literal>\w</literal>,
не используют Unicode-свойства.
</para>
</section>
<section xml:id="regexp.reference.anchors">
<title>Якоря</title>
<para>
Метасимвол начала строки <literal>^</literal> вне символьного класса
по умолчанию соответствует началу обрабатываемых данных,
если не установили модификаторы. Внутри символьного класса метасимвол
<literal>^</literal> несёт совершенно другое значение.
</para>
<para>
Циркумфлекс <literal>^</literal> не обязан быть первым символом
шаблона, если в шаблоне задействовали несколько альтернатив,
но должен быть первым символом в каждой альтернативе, в которой
он появляется, если шаблон когда-либо будет соответствовать этой ветви.
Если все альтернативы начинаются с циркумфлекса <literal>^</literal>,
то есть шаблон ограничен совпадением только с началом входной строки,
говорят что шаблон «заякорен». Есть и другие способы «заякорить» шаблон.
</para>
<para>
Знак доллара <literal>$</literal> — утверждение, которое истинно только тогда,
когда текущая точка сопоставления находится в конце входной строки, или
непосредственно перед последним символом, в случае если последний символ —
перевод строки, если не указали модификаторы.
Метасимвол конца строки <literal>$</literal> не обязан быть последним символом шаблона,
если в шаблоне задействовали несколько альтернатив, но должен быть последним символом
в каждой альтернативе, в которой он фигурирует. Внутри символьного класса
символ «$» не несёт специального значения.
</para>
<para>
Поведение метасимвола конца строки изменяют модификатором
<link linkend="reference.pcre.pattern.modifiers">PCRE_DOLLAR_ENDONLY</link> так,
чтобы метасимвол соответствовал исключительно концу строки. Данный флаг никак не
касается специальной последовательности \Z.
</para>
<para>
Значение метасимволов начала и конца строки меняется, если установили
модификатор <link linkend="reference.pcre.pattern.modifiers">PCRE_MULTILINE</link>.
В этой ситуации, кроме совпадений в начале или в конце строки,
метасимволы «^» и «$» соответствуют позиции непосредственно после символа
перевода строки «\n». Например, шаблон /^abc$/ встречается в строке «def\nabc»
в многострочном режиме и не встречается в нормальном режиме.
Таким образом, шаблон который «заякорен» в однострочном режиме, все ветки которого
начинаются с циркумфлекса «^», не будет признан «заякоренным» в многострочном режиме.
Парсер игнорирует модификатор <link linkend="reference.pcre.pattern.modifiers">PCRE_DOLLAR_ENDONLY</link>,
если установили модификатор <link linkend="reference.pcre.pattern.modifiers">PCRE_MULTILINE</link>.
</para>
<para>
Следует заметить, что служебные последовательности \A, \Z и \z
указывают для сопоставления с началом или концом строки в обоих
режимах. И если все ветви шаблона начинаются с \A, шаблон будет «заякорен»
независимо от присутствия модификатора
<link linkend="reference.pcre.pattern.modifiers">PCRE_MULTILINE</link>.
</para>
</section>
<section xml:id="regexp.reference.dot">
<title>Метасимвол точка</title>
<para>
Вне символьного класса точка соответствует любому (в том числе и непечатаемому,
бинарному) символу, кроме символа перевода строки «\n» (в обычном режиме).
Если задан модификатор
<link linkend="reference.pcre.pattern.modifiers">PCRE_DOTALL</link>,
точка соответствует также символу перевода строки.
Обработка метасимвола «точка» никак не связана с метасимволами начала и конца
строки, единственное, что у них общего, — так это специальное отношение
к символу перевода строки. Внутри символьного класса точка не имеет
специального значения.
</para>
<para>
Для совпадения с одним байтом можно пользоваться последовательностью символов <emphasis>\C</emphasis>.
Это может быть полезно <emphasis>в режиме UTF-8</emphasis>, так как с точкой
в этом режиме будет совпадать целый символ, который может состоять
из нескольких байтов.
</para>
</section>
<section xml:id="regexp.reference.character-classes">
<title>Символьные классы</title>
<para>
Открывающая квадратная скобка объявляет начало символьного класса,
который завершают квадратной скобкой. Символ «]» не имеет специального
значения, и если закрывающая квадратная скобка необходима как член
символьного класса, она должна быть первым символом непосредственно после
открывающей квадратной скобки (если указан метасимвол «^», то
непосредственно после него), либо экранироваться обратным слешем.
</para>
<para>
Символьный класс соответствует одиночному символу входной строки;
символ должен входить в набор символов, который определили в классе,
если только первый символ в классе не циркумфлекс «^»,
тогда символ входной строки не должен входить в класс. Если циркумфлекс «^»
нужен как член класса, проверяют, чтобы он не шёл первым символом в описании класса,
либо экранируют циркумфлекс обратным слешем.
</para>
<para>
Символьный класс [aeiou], например, соответствует любой гласной букве
в нижнем регистре, тогда как класс [^aeiou] соответствует любому согласному
символу нижнего регистра.
Обратите внимание, что циркумфлекс — просто удобный способ определить символьный
класс за счёт перечисления тех символов, которые не должны входить в класс.
Символ начала строки — не утверждение: он по-прежнему использует символ из входной строки
и завершается ошибкой, если текущий указатель находится в конце строки.
</para>
<para>
При регистронезависимом сопоставлении
буквы символьного класса соответствует версии символа как в верхнем,
так и в нижнем регистре. Поэтому символьный класс [aeiou]
соответствует как букве «A», так и букве «a».
Аналогично, класс [^aeiou] не соответствует ни «A», ни «a», тогда как в
регистрозависимом режиме совпадение бы состоялось.
</para>
<para>
Внутри символьного класса у символа перевода строки «\n» нет специального
значения, независимо от наличия модификаторов
<link linkend="reference.pcre.pattern.modifiers">PCRE_DOTALL</link> и
<link linkend="reference.pcre.pattern.modifiers">PCRE_MULTILINE</link>.
Символьные классы, построенные на отрицании, например [^a], всегда
соответствуют символу перевода строки.
</para>
<para>
Символ минус «-» (дефис) внутри класса используется для задания
символьного диапазона. Например, [d-m] соответствует любому символу,
находящемуся между «d» и «m», включая сами символы «d» и «m».
Если «-» необходим, как член класса,
он должен находиться в такой позиции, в которой он не может интерпретироваться
как диапазон (как правило, это первый и последний символ описания класса),
либо экранироваться при помощи обратного слеша.
</para>
<para>
Недопустимо записывать закрывающую квадратную скобку «]» как границу
символьного диапазона. Например, парсер интерпретирует шаблон «[W-]46]»
как символьный класс, который состоит из двух символов: «W» и «-», за которыми
идёт строка «46]», поэтому шаблон будет соответствовать
строкам «W46]» или «-46]».
Чтобы всё же задать символ «]» в описании диапазона, его нужно
заэкранировать обратным слешем, например, парсер интерпретирует
шаблон [W-\]46] как символьный класс, который состоит из символьного диапазона
вместе с двумя последующими символами «4» и «6».
Такого же результата можно достичь через шестнадцатеричное
или восьмеричное представление символа «]».
</para>
<para>
Диапазоны символьных классов определяют последовательностью ASCII-символов,
которые указывают через символ «-». Диапазоны также разрешено определять числами,
например [\000-\037]. Диапазон будет соответствовать буквам в нижнем и верхнем регистрах,
если в диапазон включили буквы и установили регистронезависимое сопоставление. Например,
диапазоны [W-c] и [][\^_`wxyzabc] эквивалентны, парсер сопоставляет символы
без учёта регистра, а если установлена таблица символов для французской локали «fr»,
парсер будет сопоставлять символы из диапазона [\xc8-\xcb] ударному «E» в обоих регистрах.
</para>
<para>
Типам символов \d, \D, \s, \S, \w и \W также разрешено присутствовать
в символьных классах и добавлять символы, которые им соответствуют, в класс.
Например, класс [\dABCDEF] соответствует
любой шестнадцатеричной цифре. Символ «^» указывают с типами символов
в верхнем регистре, чтобы указать более узкий набор символов. Например,
класс [^\W_] соответствует любой букве или цифре, но не символу подчёркивания.
</para>
<para>
Все небуквенно-цифровые символы, кроме \, -, ^ в начале и символа «]» в конце,
не относятся к специальным символам в символьном классе, но экранирующий
слеш перед ними не навредит. Символ конца шаблона — всегда специальный символ
и должен быть заэкранирован внутри выражения.
</para>
<para>
Язык Perl поддерживает нотацию POSIX для символьных классов. Это включает
имена в квадратных скобках: <literal>[:</literal> и <literal>:]</literal>.
Модуль PCRE также поддерживает эту тип записи. Например, шаблон
<literal>[01[:alpha:]%]</literal> совпадёт с «0», «1», любым алфавитным символом
или символом «%». Модуль PCRE поддерживает следующие имена классов:
<table>
<title>Символьные классы</title>
<tgroup cols="2">
<tbody>
<row><entry><literal>alnum</literal></entry><entry>буквы и цифры</entry></row>
<row><entry><literal>alpha</literal></entry><entry>буквы</entry></row>
<row><entry><literal>ascii</literal></entry><entry>символы с кодами 0127</entry></row>
<row><entry><literal>blank</literal></entry><entry>только пробел или символ табуляции</entry></row>
<row><entry><literal>cntrl</literal></entry><entry>управляющие символы</entry></row>
<row><entry><literal>digit</literal></entry><entry>десятичные цифры (то же самое, что и \d)</entry></row>
<row><entry><literal>graph</literal></entry><entry>печатные символы, исключая пробел</entry></row>
<row><entry><literal>lower</literal></entry><entry>строчные буквы</entry></row>
<row><entry><literal>print</literal></entry><entry>печатные символы, включая пробел</entry></row>
<row><entry><literal>punct</literal></entry><entry>печатные символы, исключая буквы и цифры</entry></row>
<row><entry><literal>space</literal></entry><entry>пробельные символы(почти то же самое, что и \s)</entry></row>
<row><entry><literal>upper</literal></entry><entry>прописные буквы</entry></row>
<row><entry><literal>word</literal></entry><entry>символы «слова» (то же самое, что и \w)</entry></row>
<row><entry><literal>xdigit</literal></entry><entry>шестнадцатеричные цифры</entry></row>
</tbody>
</tgroup>
</table>
Класс пробельных символов (<literal>space</literal>) — это горизонтальная табуляция (HT, 9),
перевод строки (LF, 10), вертикальная табуляция (VT, 11), разрыв страницы (FF, 12),
возврат каретки (CR, 13) и пробел (32). Учтите, что этот список включает
вертикальную табуляцию (VT, код 11). Это отличает «space» от <literal>\s</literal>,
который не включает этот символ (для совместимости с Perl).
</para>
<para>
Название <literal>word</literal> — это модуль Perl, а <literal>blank</literal>
модуль GNU, начиная с версии Perl 5.8. Другой модуль Perl —
отрицание, которое указывается символом <literal>^</literal> после
двоеточия. Например, <literal>[12[:^digit:]]</literal> совпадёт с «1», «2»,
или с любой не-цифрой.
</para>
<para>
В режиме UTF-8 символы со значениями, которые превышают 128, не совпадут ни с одним
из символьных классов POSIX.
Начиная с libpcre 8.10 некоторые символьные классы изменили, чтобы
использовать свойства символов Unicode, в этом случае упомянутое ограничение не применяется.
Подробнее об этом рассказывает <link xlink:href="&url.pcre.man;">руководство PCRE(3)</link>.
</para>
<para>
Свойства символов Unicode могут возникнуть внутри символьного класса.
Они не могут быть частью диапазона. Символ минус (дефис), после символьного
класс Unicode будет совпадать буквально. Попытка закончить диапазон
свойством символа Unicode вызовет предупреждение.
</para>
</section>
<section xml:id="regexp.reference.alternation">
<title>Альтернативный выбор</title>
<para>
Символ вертикальной черты «|» указывают для разделения альтернативных масок.
Например, шаблон <literal>gilbert|sullivan</literal> соответствует как
«gilbert», так и «sullivan». Допустимо указывать любое количество альтернатив,
также допустимо указывать пустые альтернативы (соответствуют пустой строке).
В процессе поиска соответствия просматриваются все перечисленные альтернативы
слева направо, останавливаясь после первого найденного соответствия.
Если альтернативные варианты перечислили в подшаблоне, то весь шаблон совпадёт
только при соответствии одного из альтернативных вариантов подшаблона
и остатка основного шаблона.
</para>
<para>
Можно записать, какая альтернатива была найдена,
используя <literal>(*MARK:NAME)</literal> или <literal>(*:NAME)</literal>.
Может появиться любое количество таких глаголов <literal>(*MARK)</literal>,
и их имена не обязательно должны быть уникальными.
Когда совпадение найдено, имя последнего встреченного <literal>(*MARK:NAME)</literal>
будет помещено среди совпадений, как если бы оно было группой захвата
с именем <literal>MARK</literal>, так что его можно будет прочитать
из параметра <parameter>matches</parameter> функции <function>preg_match</function>
и оно будет передано в параметр <parameter>callback</parameter>
функции <function>preg_replace_callback</function> и т. д.
</para>
</section>
<section xml:id="regexp.reference.internal-options">
<title>Установка внутренних опций</title>
<para>
Установки модификаторов <link linkend="reference.pcre.pattern.modifiers">PCRE_CASELESS</link>,
<link linkend="reference.pcre.pattern.modifiers">PCRE_MULTILINE</link>,
<link linkend="reference.pcre.pattern.modifiers">PCRE_DOTALL</link>,
<link linkend="reference.pcre.pattern.modifiers">PCRE_UNGREEDY</link>,
<link linkend="reference.pcre.pattern.modifiers">PCRE_EXTRA</link>,
<link linkend="reference.pcre.pattern.modifiers">PCRE_EXTENDED</link>
и PCRE_DUPNAMES, которые влияют на шаблон глобально, переопределяют внутри шаблона
буквами внутренних опций языка Perl, которые указывают между символами «(?» и «)». Буквы опций:
<table>
<title>Буквы внутренних опций</title>
<tgroup cols="2">
<tbody>
<row>
<entry><literal>i</literal></entry>
<entry><link linkend="reference.pcre.pattern.modifiers">PCRE_CASELESS</link></entry>
</row>
<row>
<entry><literal>m</literal></entry>
<entry><link linkend="reference.pcre.pattern.modifiers">PCRE_MULTILINE</link></entry>
</row>
<row>
<entry><literal>s</literal></entry>
<entry><link linkend="reference.pcre.pattern.modifiers">PCRE_DOTALL</link></entry>
</row>
<row>
<entry><literal>x</literal></entry>
<entry><link linkend="reference.pcre.pattern.modifiers">PCRE_EXTENDED</link></entry>
</row>
<row>
<entry><literal>U</literal></entry>
<entry><link linkend="reference.pcre.pattern.modifiers">PCRE_UNGREEDY</link></entry>
</row>
<row>
<entry><literal>X</literal></entry>
<entry><link linkend="reference.pcre.pattern.modifiers">PCRE_EXTRA</link> (не поддерживается с PHP 7.3)</entry>
</row>
<row>
<entry><literal>J</literal></entry>
<entry><link linkend="reference.pcre.pattern.modifiers">PCRE_INFO_JCHANGED</link></entry>
</row>
</tbody>
</tgroup>
</table>
</para>
<para>
Шаблон (?im), например, устанавливает регистронезависимое многострочное сопоставление.
Перед опцией, которую нужно сбросить, ставят символ «-» или комбинируют
установку и отмену режимов. Запись (?im-sx), например, устанавливает флаги
<link linkend="reference.pcre.pattern.modifiers">PCRE_CASELESS</link>,
<link linkend="reference.pcre.pattern.modifiers">PCRE_MULTILINE</link>
и отменяет флаги <link linkend="reference.pcre.pattern.modifiers">PCRE_DOTALL</link>
и <link linkend="reference.pcre.pattern.modifiers">PCRE_EXTENDED</link>.
Модуль отменит опцию, если символ расположили одновременно до и после
символа «-».
</para>
<para>
Парсер применит изменение к оставшейся части шаблона,
если опцию изменяют на верхнем уровне (т. е. вне круглых скобок подшаблона).
Поэтому шаблон <literal>/ab(?i)c/</literal> совпадёт только со значениями «abc» и «abC».
</para>
<para>
Эффект будет другим, если опцию изменяют внутри подшаблона.
Это изменение поведения в Perl 5.005. Изменение опции внутри подшаблона повлияет
только на ту часть шаблона, которая следует за ним, то есть шаблон
<literal>(a(?i)b)c</literal>
совпадёт с «abc» и «aBc» и больше ни с чем (разумеется, если
не включили режим <link linkend="reference.pcre.pattern.modifiers">PCRE_CASELESS</link>).
Это означает, что опции умеют задавать разные настройки в разных частях шаблона.
Изменения в одной альтернативе переносятся и в другие ветки в пределах того же подшаблона.
Например, шаблон
<literal>(a(?i)b|c)</literal>
совпадёт с «ab», «aB», «c», и «C», хотя и при совпадении с «C» первая ветка
была отброшена до установки опции. Это происходит потому, что модуль устанавливает
опции на этапе компиляции. В противном случае поведение было бы странным.
</para>
<para>
Специфичные для модуля PCRE опции, например
<link linkend="reference.pcre.pattern.modifiers">PCRE_UNGREEDY</link>
и <link linkend="reference.pcre.pattern.modifiers">PCRE_EXTRA</link>,
разрешено устанавливать так же, как и Perl-совместимые опции, —
через символы U и X.
Установка флага (?X) отличается тем, что должен быть расположен в шаблоне прежде, чем будет
установлена любая другая дополнительная возможность, даже если его расположили
на верхнем уровне. Рекомендовано размещать флаг (?X) в самом начале шаблона.
</para>
</section>
<section xml:id="regexp.reference.subpatterns">
<title>Подшаблоны</title>
<para>
Подшаблоны ограничивают круглыми скобками, которым разрешено быть вложенными.
Пометка части шаблона как подшаблона выполняет две функции:
</para>
<orderedlist>
<listitem>
<para>
Локализирует набор альтернатив. Например, шаблон
<literal>cat(aract|erpillar|)</literal> соответствует одному из слов «cat»,
«cataract» или «caterpillar». Без скобок он соответствовал
бы строкам «cataract», «erpillar» или пустой строке.
</para>
</listitem>
<listitem>
<para>
Настраивает подшаблон как подшаблон захвата подстроки, как показывает первый пункт.
Функция модуля PCRE <function>pcre_exec</function> через аргумент <emphasis>ovector</emphasis>
возвращает вызывающей функции подстроку, которая соответствует подшаблону,
когда совпадает полный шаблон.
Функция подсчитывает открывающие круглые скобки с единицы слева направо,
чтобы получить количество захватывающих подшаблонов.
</para>
</listitem>
</orderedlist>
<para>
Например, если строка «the red king» сопоставляется с шаблоном
<literal>the ((red|white) (king|queen))</literal>,
парсер захватит подстроки «red king», «red» и «king» с номерами 1, 2 и 3
соответственно.
</para>
<para>
В реальной жизни выполнение одновременно двух функций может оказаться неудобным.
Бывают случаи, когда нужна группировка альтернатив без захвата строки.
Если после открывающей круглой скобки идут символы «?:», захват строки
не происходит, и текущий подшаблон не нумеруется.
Например, если строка «the white queen» сопоставляется с шаблоном
<literal>the ((?:red|white) (king|queen))</literal>,
парсер захватит подстроки «white queen» и «queen» и пронумерует подстроки числами
1 и 2 соответственно. Максимальное количество захватывающих подшаблонов — 65 535.
Такие большие шаблоны могут не скомпилироваться, в зависимости от настроек модуля libpcre.
</para>
<para>
Если в незахватывающем подшаблоне нужно указать дополнительные
опции, пользуются удобным сокращением: символ, который обозначает
устанавливаемую опцию, помещается между «?» и «:».
Таким образом, следующие два шаблона
</para>
<informalexample>
<programlisting>
<![CDATA[
(?i:saturday|sunday)
(?:(?i)saturday|sunday)
]]>
</programlisting>
</informalexample>
<para>
соответствуют одному и тому же набору строк. Поскольку альтернативные
версии берутся слева направо, и установленные опции сохраняют своё
действие до конца подшаблона, опция, установленная в одной ветке, также
имеет эффект во всех последующих ветках. Поэтому приведённые шаблоны
совпадают как с «SUNDAY», так и с «Saturday».
</para>
<para>
Именованные подшаблона задают синтаксисом
<literal>(?P&lt;name&gt;pattern)</literal>. Этот подшаблон будет индексирован
в массиве совпадений кроме обычного числового индекса, ещё и по имени name.
Именованные подшаблоны записывают двумя альтернативными синтаксисами:
<literal>(?&lt;name&gt;pattern)</literal> и <literal>(?'name'pattern)</literal>.
</para>
<para>
Иногда необходимо иметь несколько совпадений, которые исключают друг друга.
Обычно, каждое такое совпадение получает свой собственный номер, даже
если шаблон разрешает совпасть только одному из них.
Синтаксис <literal>(?|</literal> даёт обойти это поведение и убрать
дублирующиеся номера. Рассмотрим следующее регулярное выражение,
сопоставленное со строкой <literal>Sunday</literal>:
</para>
<informalexample>
<programlisting>
<![CDATA[(?:(Sat)ur|(Sun))day]]>
</programlisting>
</informalexample>
<para>
Здесь парсер сохраняет значение <literal>Sun</literal> в ссылке 2, тогда как
ссылка 1 пуста. В результате сопоставления <literal>Saturday</literal>
в обратной ссылке 1 появляется <literal>Sat</literal>,
в то время как обратная ссылка 2 не существует.
Запись <literal>(?|</literal> в шаблоне решает эту проблему:
</para>
<informalexample>
<programlisting>
<![CDATA[(?|(Sat)ur|(Sun))day]]>
</programlisting>
</informalexample>
<para>
В этом шаблоне оба подшаблона <literal>Sun</literal> и <literal>Sat</literal>
парсер сохранят под номером 1.
</para>
</section>
<section xml:id="regexp.reference.repetition">
<title>Повторение</title>
<para>
Повторение определяется квантификаторами, которые идут за любым из указанных
элементов:
<itemizedlist>
<listitem><simpara>одним символом, возможно, экранированным</simpara></listitem>
<listitem><simpara>метасимволом «точка»</simpara></listitem>
<listitem><simpara>символьным классом</simpara></listitem>
<listitem><simpara>ссылкой на предыдущий фрагмент шаблона (смотрите следующий раздел)</simpara></listitem>
<listitem><simpara>взятым в круглые скобки подшаблоном (если это не утверждение — смотрите далее)</simpara></listitem>
</itemizedlist>
</para>
<para>
Общий квантификатор повторения указывает минимальное и максимальное
допустимое количество совпадений, согласно двум числам
в фигурных скобках, разделённым запятой. Числа должны быть меньше чем 65 536,
и первое число не должно превышать второе по значению.
Например:
<literal>z{2,4}</literal>
соответствует «zz», «zzz» или «zzzz». Закрывающая фигурная скобка сама
по себе — не специальный символ. Если второе число опустили,
но запятую поставили, нет верхнего предела; Если и второе
число, и запятую опустили, требуется точное число повторений.
Поэтому шаблон
<literal>[aeiou]{3,}</literal>
соответствует как минимум трём последовательным гласным, а также любому
количеству гласных выше трёх, тогда как шаблон
<literal>\d{8}</literal>
соответствует ровно восьми цифрам.
</para>
<simpara>
До PHP 8.4.0 открывающая фигурная скобка, которая появляется в месте,
где квантификатор не допускается или которая не соответствует синтаксису квантификатора,
рассматривается как литеральный символ.
Например, <literal>{,6}</literal> не является квантификатором,
а представляет собой литеральную строку из четырёх символов.
Начиная с PHP 8.4.0, модуль PCRE поставляется в комплекте с PCRE2 версии 10.44,
которое допускает такие шаблоны, как <literal>\d{,8}</literal>
и они интерпретируются как <literal>\d{0,8}</literal>.
Кроме того, начиная с PHP 8.4.0, допускаются пробелы вокруг квантификаторов,
такие как <literal>\d{0 , 8}</literal> и <literal>\d{ 0 , 8 }</literal>.
</simpara>
<para>
Квантификатор {0} — допустим и ведёт себя таким образом, будто бы
сам квантификатор и предшествующий ему элемент отсутствуют.
</para>
<para>
Для удобства, а так же обратной совместимости, у трёх наиболее распространённых
квантификатора односимвольные аббревиатуры:
<table>
<title>Односимвольные квантификаторы</title>
<tgroup cols="2">
<tbody>
<row>
<entry><literal>*</literal></entry>
<entry>эквивалентен <literal>{0,}</literal></entry>
</row>
<row>
<entry><literal>+</literal></entry>
<entry>эквивалентен <literal>{1,}</literal></entry>
</row>
<row>
<entry><literal>?</literal></entry>
<entry>эквивалентен <literal>{0,1}</literal></entry>
</row>
</tbody>
</tgroup>
</table>
</para>
<para>
Можно конструировать бесконечные циклы, указав после шаблона, который совпадает
с пустой строкой, квантификатор без верхнего предела, например:
<literal>(a?)*</literal>
</para>
<para>
Ранние версии языка Perl и модуля PCRE выдавали ошибку во время компиляции для таких
шаблонов. Однако, поскольку бывают случаи, когда подобные шаблоны могли бы
быть полезны, была добавлена поддержка таких шаблонов. Но если любое повторение
такого подшаблона фактически не совпадает ни с какими символами, цикл
принудительно прерывается.
</para>
<para>
По умолчанию все квантификаторы — «жадные», это означает, что они
совпадают максимально возможное количество раз (но не более, чем максимально
допустимое), и не останавливают сопоставление
остальных частей шаблона. Классический пример проблем, которые
могут возникнуть в связи с такой особенностью квантификаторов, —
попытка найти комментарии в программах, которые написали на языке C.
Комментарием признаётся произвольный текст внутри символьных комбинаций /* и */
(при этом, символы «/» и «*» тоже могут быть частью комментария). Попытка найти комментарии
через шаблон
<literal>/\*.*\*/</literal>
в строке
<literal>/* первый комментарий */ не комментарий /* второй комментарий */</literal>
закончится неудачей, поскольку указанный шаблон соответствует всей строке
целиком (из-за жадности квантификатора «*»).
</para>
<para>
Однако, если сразу же после квантификатора идёт вопросительный знак, квантификатор
становится «ленивым» и соответствует минимально допустимому количеству раз.
Поэтому шаблон
<literal>/\*.*?\*/</literal>
корректно находит комментарии языка C. Знак «?» после
квантификатора влияет только на его жадность, и не влияет
на другие свойства. Не нужно путать
квантификатор «?» (ноль или одно соответствие) ограничитель
жадности. Из-за его двойственности пользуются
следующей записью:
<literal>\d??\d</literal>,
которая, в первую очередь, соответствует одной цифре, но также
может соответствовать и двум цифрам, если это необходимо для
соответствия остальных частей шаблона.
</para>
<para>
Если установили опцию
<link linkend="reference.pcre.pattern.modifiers">PCRE_UNGREEDY</link>
(которой нет в языке Perl), квантификаторы перестают быть жадными по умолчанию,
но могут становиться жадными, если за ними идёт символ «?».
Говоря другими словами, знак вопроса инвертирует жадность
квантификаторов.
</para>
<para>
Квантификаторы, за которыми идёт символ <literal>+</literal>, — «захватывающие».
Они поглощают столько символов, сколько могут, и не возвращаются обратно
для совпадения остатка шаблона. Поэтому шаблон <literal>.*abc</literal>
совпадёт с «aabc», а <literal>.*+abc</literal> — нет, потому что
подшаблон <literal>.*+</literal> захватит всю строку целиком. Захватывающими
квантификаторами пользуются для ускорения обработки.
</para>
<para>
Если задали подшаблон с квантификатором, для которого установили минимальное
количество повторений (больше одного), либо задали максимальное количество
повторений, то для откомпилированного шаблона требуется больше памяти
(пропорционально минимуму либо максимуму соответственно).
</para>
<para>
Если шаблон начинается с символов .* либо .{0,} и установили модификатор
<link linkend="reference.pcre.pattern.modifiers">PCRE_DOTALL</link>
(аналог Perl-опции /s), который разрешает метасимволу
«точка» соответствовать переводам строк, шаблон неявно заякоривается.
Это происходит потому, что все последующие конструкции парсер будет сопоставлять
с каждой символьной позицией в обрабатываемом тексте, и, как следствие,
начало строки — единственная позиция, которая даёт наиболее полное совпадение.
Модуль PCRE рассматривает каждый такой шаблон, как если бы ему предшествовала
последовательность \A. Если известно, что данные
не содержат переводов строк, а сам шаблон начинается с символов .*, рекомендуется
использовать модификатор <link linkend="reference.pcre.pattern.modifiers">PCRE_DOTALL</link>
для оптимизации шаблона, либо указывать метасимвол «^» для явного заякоривания.
</para>
<para>
Если захватывающий подшаблон повторяется, результирующим значением
подшаблона будет подстрока, которая совпадает с результатом последней итерации.
Например, когда шаблон
<literal>(tweedle[dume]{3}\s*)+</literal>
совпадёт со значением «tweedledum tweedledee», результирующим значением подшаблона
будет значение «tweedledee». Однако, если присутствуют вложенные захватывающие
подшаблоны, значения, которые соответствуют этим подшаблонам, допускается устанавливать
в предыдущих итерациях. Например, когда шаблон
<literal>/(a|(b))+/</literal>
совпадёт со значением «aba», значением второй захваченной подстроки будет значение «b».
</para>
</section>
<section xml:id="regexp.reference.back-references">
<title>Обратные ссылки</title>
<para>
Вне символьного класса модуль интерпретирует обратный слеш, за которым идёт
цифра больше 0 (и, возможно, дополнительные цифры),
как обратную ссылку на подшаблон, который раньше в шаблоне захватили круглые скобки,
при условии, что было столько предыдущих захватывающих открывающих круглых скобок.
</para>
<para>
Однако, если число, которое следует за обратным слешем, меньше 10,
оно всегда интерпретируется как обратная ссылка, и приводит к ошибке
только тогда, когда нет соответствующего числа открывающих
скобок. Другими словами, открывающие скобки не обязаны предшествовать
ссылке для чисел меньше 10. «Упреждающая обратная ссылка» может
иметь смысл, если используется повторение и более поздний подшаблон
участвует в ранней итерации.
Раздел «<link linkend="regexp.reference.escape">Экранирующие последовательности</link>»
даёт дополнительные сведения об обработке цифр, которые идут за обратным слешем.
</para>
<para>
Обратная ссылка сопоставляется с частью строки, которую захватил
соответствующий подшаблон, но не с самим подшаблоном.
Поэтому шаблон
<literal>(sens|respons)e and \1ibility</literal>
соответствует значениям «sense and sensibility» и «response and responsibility»,
но не «sense and responsibility». При сопоставлении обратной ссылки парсер также
будет учитывать регистр, если обнаружит обратную ссылку
во время регистрозависимого поиска. Например, шаблон
<literal>((?i)rah)\s+\1</literal>
соответствует значениям «rah rah» и «RAH RAH», но не «RAH rah», хотя сам
подшаблон сопоставляется без учёта регистра.
</para>
<para>
На один и тот же подшаблон разрешено устанавливать несколько ссылок. Если
подшаблон не участвовал в сопоставлении, то сопоставление со
ссылкой на неё всегда терпит неудачу. Например, шаблон
<literal>(a|(bc))\2</literal>
терпит неудачу, если находит соответствие с «a» раньше, чем с «bc».
Поскольку может быть до 99 обратных ссылок, все цифры, следующие
за обратным слешем, рассматриваются как часть потенциальной
обратной ссылки. Если за ссылкой должна следовать цифра, необходимо
использовать ограничитель. Если указан флаг
<link linkend="reference.pcre.pattern.modifiers">PCRE_EXTENDED</link>,
ограничителем может быть любой пробельный символ. В противном
случае можно использовать пустой комментарий.
</para>
<para>
Ссылка на подшаблон, внутри которого она расположена, терпит неудачу,
если это первое сопоставление текущего подшаблона. Например, шаблон (a\1)
не соответствует ни одной строке. Но всё же такие ссылки бывают
полезны в повторяющихся подшаблонах. Например, шаблон
<literal>(a|b\1)+</literal>
совпадает с любым количеством «a», «aba», «ababaa»... При
каждой итерации подшаблона обратная ссылка соответствует той части
строки, которая была захвачена при предыдущей итерации.
Чтобы такая конструкция работала, шаблон должен быть построен так,
чтобы при первой итерации сопоставление с обратной ссылкой не производилось.
Этого можно достичь, используя альтернативы (как в предыдущем
примере), либо квантификаторы с минимумом, равным нулю.
</para>
<para>
Управляющую последовательность <literal>\g</literal>
указывают для абсолютных и относительных ссылок на подшаблоны.
После этой последовательности должно быть указано
беззнаковое или отрицательное число, при желании заключённое в фигурные
скобки. Последовательности <literal>\1</literal>, <literal>\g1</literal>
и <literal>\g{1}</literal> эквивалентны друг другу.
Использование этого шаблона с беззнаковым числом поможет избежать
двусмысленности, присущей числам после обратного слеша. Это также
помогает отличить обратные ссылки от символов в восьмеричном
формате, а также упрощает запись числового литерала сразу после
обратной ссылки, например, <literal>\g{2}1</literal>.
</para>
<para>
Использование отрицательных чисел с <literal>\g</literal> полезно при
использовании относительных ссылок. Например, <literal>(foo)(bar)\g{-1}</literal>
соответствует «foobarbar», а <literal>(foo)(bar)\g{-2}</literal>
соответствует «foobarfoo». Это также может быть полезно в длинных
шаблонах, как альтернатива отслеживания числа подшаблонов,
на которые можно ссылаться в последующей части шаблона.
</para>
<para>
Обратную ссылку на именованный подшаблон указывают следующими записями:
<literal>(?P=name)</literal>,
<literal>\k&lt;name&gt;</literal>, <literal>\k'name'</literal>, <literal>\k{name}</literal>,
<literal>\g{name}</literal>, <literal>\g&lt;name&gt;</literal> или <literal>\g'name'</literal>.
</para>
</section>
<section xml:id="regexp.reference.assertions">
<title>Утверждения</title>
<para>
Утверждения — проверка символов, которые идут до или после текущей позиции
сопоставления, которая фактически не включает никаких символов (символы входной
строки не сопоставляются с утверждениями). Наиболее простые варианты утверждений,
например \b, \B, \A, \Z, \z, ^ и $, рассматривает раздел
«<link linkend="regexp.reference.escape">Экранирующие последовательности</link>».
Более сложные утверждения записываются как подшаблоны. Утверждения
бывают двух видов: те, которые анализируют текст, следующий
<emphasis>после текущей позиции</emphasis> (look ahead),
и идущий <emphasis>перед ней</emphasis> (look behind).
</para>
<para>
Сопоставление подшаблонов, которые содержат утверждение, выполняют обычным
образом, за исключением того, что текущая позиция не изменяется.
Утверждения касательно <emphasis>последующего текста</emphasis>
начинаются с (?= для положительных утверждений и с (?! для отрицающих
утверждений. Например,
<literal>\w+(?=;)</literal>
совпадает со словом, за которым следует символ «;», но при этом сама
точка с запятой в совпадение не включается. А
<literal>foo(?!bar)</literal>
соответствует любому появлению «foo», после которого не идёт «bar».
Заметим, что похожий шаблон
<literal>(?!foo)bar</literal>
не будет искать вхождение «bar», которому предшествует любая
строка за исключением «foo». Но, тем не менее, он будет соответствовать
любым вхождениям подстроки «bar», поскольку условие (?!foo) всегда
&true;, если следующие три символа — «bar». Для получения желаемого
результата необходимо воспользоваться второй категорией утверждений.
</para>
<para>
Утверждения касательно <emphasis>предшествующего текста</emphasis>
начинаются с (?&lt;= для положительных утверждений и (?&lt;!
для отрицающих. Например,
<literal>(?&lt;!foo)bar</literal>
найдёт вхождения «bar», которым не предшествует «foo». Сами
утверждения «назад» ограничены так, чтобы все подстроки, которым
они соответствуют, имели фиксированную длину. Но, Если
используются несколько альтернатив, они не обязаны иметь одинаковую
длину. Таким образом шаблон
<literal>(?&lt;=bullock|donkey)</literal>
корректен, но
<literal>(?&lt;!dogs?|cats?)</literal>
вызовет ошибку во время компиляции. Ветки, которые соответствуют
строкам разной длины, разрешены только на верхнем уровне утверждений
касательно предшествующего текста. Это расширение относительно
Perl 5.005, который требует чтобы все ветки соответствовали строкам
одинаковой длины. Такое утверждение как
<literal>(?&lt;=ab(c|de))</literal>
не корректно, поскольку верхний уровень маски может соответствовать
строкам разной длины, но его можно преобразовать к корректному шаблону,
используя альтернативы на верхнем уровне:
<literal>(?&lt;=abc|abde)</literal>
Утверждения касательно предшествующего текста реализованы так,
что для каждой альтернативы текущая позиция временно переносится
назад, на фиксированную ширину, после чего выполняется поиск
соответствия условию. Если перед текущей позицией недостаточно
символов, поиск соответствия терпит неудачу. Утверждения назад в сочетании
с однократными подшаблонами удобны для поиска
в конце строки; соответствующий пример приведён в конце раздела
«Однократные подшаблоны».
</para>
<para>
Несколько утверждений (разных типов) могут присутствовать в
утверждении, например:
<literal>(?&lt;=\d{3})(?&lt;!999)foo</literal>
совпадает с подстрокой «foo», которой предшествуют три цифры,
отличные от «999». Следует понимать, что каждое из утверждений
проверяется относительно одной и той же позиции в обрабатываемом
тексте. Вначале выполняется проверка того, что предшествующие три символа —
это цифры, затем проверяется, чтобы эти же цифры не являлись
числом 999. Приведённый выше шаблон не соответствует подстроке
«foo», которой предшествуют шесть символов, первые три из которых —
цифры, а последние три не образуют «999». Например, он не
соответствует строке «123abcfoo», в то время как шаблон
<literal>(?&lt;=\d{3}...)(?&lt;!999)foo</literal>
соответствует.
</para>
<para>
В этом случае анализируются предшествующие шесть
символов на предмет того, чтобы первые три из них были цифрами,
а последние три не образовали «999».
</para>
<para>
Утверждения могут быть вложенными, причём в произвольных сочетаниях:
<literal>(?&lt;=(?&lt;!foo)bar)baz</literal>
соответствует подстроке «baz», которой предшествует «bar»,
перед которой, в свою очередь, нет «foo», а
<literal>(?&lt;=\d{3}...(?&lt;!999))foo</literal>
совершенно другой шаблон, соответствующий подстроке «foo»,
которой предшествуют три цифры и три произвольных символа, отличных
от «999».
</para>
<para>
Утверждающие подшаблоны — незахватывающие и неповторяемые,
поскольку бессмысленно повторять одно и то же несколько раз. Если
в утверждении произвольного типа находится захватывающий подшаблон,
он нумеруется в той же последовательности, что и все остальные
захватывающие подшаблоны, но захват соответствующих значений происходит
только для положительных утверждений, поскольку для отрицающих это не
имеет смысла.
</para>
<para>
В утверждениях обрабатывается не более 200 захватывающих подшаблонов.
</para>
</section>
<section xml:id="regexp.reference.onlyonce">
<title>Однократные подшаблоны</title>
<para>
Как для минимального, так и для максимального количества повторений,
если следующая часть шаблона при сопоставлении завершается неудачно,
начинается повторный анализ повторяемого выражения на предмет того,
возможно ли успешное сопоставление всего шаблона при другом количестве
повторений. Бывают случаи, когда необходимо изменить описанную логику
работы для реализации специфического сопоставления либо оптимизации шаблона
(если автор уверен, что других вариантов соответствия нет).
</para>
<para>
В качестве примера, рассмотрим шаблон \d+foo в применении к строке
<literal>123456bar</literal>
</para>
<para>
После того, как \d+ будет сопоставлен с первыми шестью цифрами,
сопоставление «foo» потерпит неудачу. После этого, в соответствие
\d+, будет сопоставлено 5 цифр, после очередной неудачи будет сопоставлено
4 цифры и так далее. В конце концов весь шаблон потерпит неудачу.
Однократные подшаблоны указывают, что если одна часть шаблона была
сопоставлена, её не стоит анализировать повторно. Применимо к приведённому
выше примеру весь шаблон потерпел бы неудачу после первого же
неудачного сопоставления с «foo». Записываются однократные шаблоны
при помощи круглых скобок следующим образом: (?&gt;. Например:
<literal>(?&gt;\d+)bar</literal>
</para>
<para>
Этот вид подшаблона предотвращает повторный анализ подшаблона, если
сопоставление последующих элементов терпит неудачу. Однако это не мешает
повторно анализировать любые другие элементы, в том числе те, которые предшествуют
однократному подшаблону.
</para>
<para>
Говоря другими словами, подшаблоны такого типа соответствуют
той части подстроки, которой соответствовала бы одиночная
изолированная маска, заякоренная на текущей позиции обрабатываемого
текста.
</para>
<para>
Однократные подшаблоны — незахватывающие. Простые примеры,
подобные приведённому, можно охарактеризовать как безусловный
захват максимального количества повторений. В то время как
\d+ и \d+? корректируются так, чтобы остальные части шаблона
так же совпали, (?&gt;\d+) соответствует исключительно максимальной по
длине последовательности цифр, даже если это приводит к неудаче при
сопоставлении других частей шаблона.
</para>
<para>
Однократным подшаблонам разрешено включать в себя более сложные конструкции
и быть вложенными.
</para>
<para>
Однократные подшаблоны разрешено указывать совместно с утверждениями, которые
касаются предыдущего текста для описания эффективных сопоставлений
в конце обрабатываемого текста. Рассмотрим простой шаблон
<literal>abcd$</literal>
в применении к длинному тексту, который не соответствует указанной маске.
Поскольку поиск происходит слева направо, вначале PCRE будет
искать букву «a», и только потом анализировать следующие
записи в шаблоне. Если шаблон указан в виде
<literal>^.*abcd$</literal>.
В этой ситуации вначале .* сопоставляется со всей строкой, после
чего сопоставление терпит неудачу (так как нет последующего символа «a»).
После чего .* сопоставляется со всей строкой, кроме последнего символа,
потом кроме двух последних символов, и так далее. В конечном итоге
поиск символа «a» происходит по всей строке. Однако, если шаблон записать
в виде:
<literal>^(?>.*)(?&lt;=abcd)</literal>
повторный анализ для .* не выполняется, и, как следствие, может
соответствовать только всей строке целиком. После чего утверждение
проверяет последние четыре символа на совпадение с «abcd», и в случае
неудачи все сопоставление терпит неудачу. Для больших объёмов
обрабатываемого текста этот подход имеет значительный выигрыш во времени
выполнения.
</para>
<para>
Если шаблон содержит неограниченное повторение внутри подшаблона,
который в свою очередь также может повторяться неограниченное количество
раз, однократные подшаблоны помогают
избегать многократных неудачных сопоставлений,
которые длятся достаточно продолжительное время. Шаблон
<literal>(\D+|&lt;\d+>)*[!?]</literal>
соответствует неограниченному количеству подстрок, которые состоят
не из цифр, либо из цифр заключённых в &lt;>, за которыми следует
? либо !. Если в обрабатываемом тексте содержатся
соответствия, время работы регулярного выражения будет невелико.
Но если его применить к строке
<literal>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</literal>
это займёт длительное время. Это связанно с тем, что строка
может быть разделена между двумя частями шаблона многими способами,
и все они будут опробованы (в примере мы использовали [?!], поскольку
в случае одиночного символа в конце шаблона и PCRE и Perl выполняют
оптимизацию. Они запоминают последний одиночный символ и в случае
его отсутствия выдают неудачу). Если изменить шаблон на
<literal>((?>\D+)|&lt;\d+>)*[!?]</literal>,
не цифровые последовательности не могут быть разорваны, и
невозможность сопоставления обнаруживается гораздо быстрее.
</para>
</section>
<section xml:id="regexp.reference.conditional">
<title>Условные подшаблоны</title>
<para>
В модуле PCRE реализована возможность подчинять шаблон условию или выбирать
из двух условных подшаблонов, в зависимости от успеха сопоставления
предыдущего подшаблона. У условных подшаблонов две допустимые формы:
</para>
<informalexample>
<programlisting>
<![CDATA[
(?(condition)yes-pattern)
(?(condition)yes-pattern|no-pattern)
]]>
</programlisting>
</informalexample>
<para>
При успешном сопоставлении условия condition будет выбран
подшаблон yes-pattern, иначе — no-pattern, если он есть.
Если указали более двух альтернатив, парсер выдаст ошибку времени компиляции.
</para>
<para>
Условия бывают двух видов. Если между скобками
заключены цифры, условие будет выполняться в том случае,
если подшаблон с соответствующим номером был успешно сопоставлен.
Рассмотрим следующий шаблон (он содержит незначащий пробел для удобства
чтения, подразумевается использование модификатора
<link linkend="reference.pcre.pattern.modifiers">PCRE_EXTENDED</link>),
разделив его для удобства на три смысловые части:
</para>
<informalexample>
<programlisting>
<![CDATA[
( \( )? [^()]+ (?(1) \) )
]]>
</programlisting>
</informalexample>
<para>
Первая часть соответствует опциональной открывающей скобке,
и в случае если она присутствует, захватывает её как значение
первого подшаблона. Следующая часть соответствует одному или более
символам, которые отличаются от круглой скобки. Третья часть — условный
подшаблон, который зависит от результата сопоставления первого подшаблона.
Если в начале обрабатываемых данных парсер обнаружил
открывающую круглую скобку, условие будет интерпретировано как
истина, и, следовательно, для успешного сопоставления третьей
части шаблона необходима закрывающая круглая скобка. В противном случае,
поскольку вторую ветвь условного шаблона не указали,
парсер сопоставит третью часть с пустой строкой. Суммируя все вышесказанное,
приведённый шаблон совпадает с последовательностью не-скобок,
возможно, заключённой в круглые скобки.
</para>
<para>
Если условие — строка <literal>(R)</literal>, парсер выполнит
условие, если будет произведён рекурсивный вызов к шаблону или
подшаблону. На «самом верхнем уровне» условие ложно.
</para>
<para>
Если условие не является последовательностью цифр или (R),
оно должно быть утверждением. Это может быть либо положительная
или отрицательная проверка последующего либо предыдущего текста.
Рассмотрим данный шаблон, снова содержащий незначащие пробелы,
с двумя альтернативами на второй строке:
</para>
<informalexample>
<programlisting>
<![CDATA[
(?(?=[^a-z]*[a-z])
\d{2}-[a-z]{3}-\d{2} | \d{2}-\d{2}-\d{2} )
]]>
</programlisting>
</informalexample>
<para>
Приведён пример с утверждающим условием касательно предшествующего
текста, которое выполняется для необязательной последовательности
не-букв с последующей буквой. Говоря другими словами, указанное
условие проверяет наличие хотя бы одной предшествующей буквы.
Если буква найдена, выполняется сопоставление с первой
альтернативой, в противном случае — со второй альтернативой.
Приведённый шаблон соответствует строкам двух видов:
dd-aaa-dd либо dd-dd-dd, где aaaa — это буквы, а dd — цифры.
</para>
</section>
<section xml:id="regexp.reference.comments">
<title>Комментарии</title>
<para>
Служебная последовательность (?# обозначает начало комментария,
который продолжается до ближайшей закрывающей скобки. Вложенные
скобки не допускаются. Символы, находящиеся внутри комментария,
не принимают участия в сопоставлении шаблона.
</para>
<para>
Если используется модификатор
<link linkend="reference.pcre.pattern.modifiers">PCRE_EXTENDED</link>,
неэкранированный символ «#» вне символьного класса также означает
начало блока комментария, который длится до конца текущей строки.
</para>
<para>
<example>
<title>Использование комментариев в шаблоне PCRE</title>
<programlisting role="php">
<![CDATA[
<?php
$subject = 'test';
/* Последовательность (?# указывают, чтобы добавить комментарий без включения модификатора PCRE_EXTENDED */
$match = preg_match('/te(?# this is a comment)st/', $subject);
var_dump($match);
/* Пробелы и символы # рассматриваются как часть шаблона, если не включён модификатор PCRE_EXTENDED */
$match = preg_match('/te #~~~~
st/', $subject);
var_dump($match);
/* Когда модификатор PCRE_EXTENDED включён, пробелы и символы,
* которые идут за незаэкранированным символом #
* в той же строке, игнорируются
*/
$match = preg_match('/te #~~~~
st/x', $subject);
var_dump($match);
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
int(1)
int(0)
int(1)
]]>
</screen>
</example>
</para>
</section>
<section xml:id="regexp.reference.recursive">
<title>Рекурсивные шаблоны</title>
<para>
Рассмотрим проблему сопоставления строки в круглых скобках, которая допускает
неограниченное количество вложенных круглых скобок.
Лучшее, что можно сделать без рекурсии, — написать шаблон, который
будет решать задачу для ограниченной глубины вложенности, поскольку обработать
неограниченную глубину невозможно.
Perl 5.6 предоставил ряд экспериментальных возможностей,
которые в том числе разрешают реализовать рекурсию в шаблонах.
Специальное обозначение (?R) задают, чтобы указать рекурсивный
подшаблон. Таким образом, приведём PCRE-шаблон, который решает поставленную
задачу (подразумевается, что включён модификатор
<link linkend="reference.pcre.pattern.modifiers">PCRE_EXTENDED</link>,
незначащие пробелы игнорируются):
<literal>\( ( (?>[^()]+) | (?R) )* \)</literal>
</para>
<para>
Вначале шаблон соответствует открывающей круглой скобке. Затем
шаблон соответствует любому количеству подстрок, каждая из которых
может быть последовательностью символов, кроме скобок, либо строкой, рекурсивно
соответствующей шаблону (т. е. строкой, корректно заключённой в
круглые скобки). И, в конце, идёт закрывающая круглая скобка.
</para>
<para>
Приведённый пример шаблона включает вложенные неограниченные повторения,
поэтому однократные шаблоны значительно ускоряют процесс
сопоставления, особенно когда строка не соответствует заданной
маске. Например, если применить этот шаблон к строке:
<literal>(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa()</literal>,
то несоответствие будет обнаружено достаточно быстро. Но без
однократных шаблонов сопоставление будет затягиваться
на длительное время, поскольку существует множество способов разделения
строки между квантификаторами + и *, и все они должны быть проверены,
прежде чем будет выдано сообщение о неудаче.
</para>
<para>
Значение, которое устанавливают для захватывающего подшаблона, будет соответствовать
значению, захваченному на наиболее глубоком уровне рекурсии. Когда приведённый шаблон
сопоставляется со строкой
<literal>(ab(cd)ef)</literal>,
захваченным значением будет «ef» — последнее значение,
принимаемое на верхнем уровне. Если добавить дополнительные скобки
<literal>\( ( ( (?>[^()]+) | (?R) )* ) \)</literal>,
захваченным значением будет «ab(cd)ef». Если
в шаблоне встречается более 15 захватывающих скобок, модулю PCRE
требуется больше памяти для обработки рекурсии, чем обычно.
Память выделяется функцией pcre_malloc и освобождается
функцией pcre_free. Если функция не может выделить память,
сохраняются данные только для первых 15 захватывающих скобок,
поскольку нет способа выдать ошибку out-of-memory изнутри рекурсии.
</para>
<para>
Конструкции <literal>(?1)</literal>, <literal>(?2)</literal> и так далее
указывают для рекурсивных подшаблонов.
Допустимо также указывать именованные подшаблоны:
<literal>(?P&gt;name)</literal> или <literal>(?&amp;name)</literal>.
</para>
<para>
Если синтаксис ссылки на рекурсивный подшаблон (как по имени, так и по
числовому индексу) указали вне скобок, к которым он относится,
парсер обработает подшаблон аналогично подпрограмме в языке программирования.
Возьмём более ранний пример, который указывает, что шаблон
<literal>(sens|respons)e and \1ibility</literal>
соответствует значениям «sense and sensibility» и «response and responsibility»,
но не «sense and responsibility». Если вместо этого написать шаблон
<literal>(sens|respons)e and (?1)ibility</literal>,
он совпадёт со значением «sense and responsibility» так же, как и с другими двумя
строками. Однако такие ссылки должны быть указаны после подшаблона, на который
они ссылаются.
</para>
<para>
Максимальная длина входной строки — наибольшее положительное число,
которое может содержать целочисленная переменная. Однако, поскольку для обработки
подшаблонов и бесконечного повторения модуль PCRE запускает рекурсию, это означает,
что размер обрабатываемых строк в некоторых шаблонах также может быть
ограничен доступным размером стека.
</para>
</section>
<section xml:id="regexp.reference.performance">
<title>Производительность</title>
<para>
Некоторые элементы, которые встречаются в шаблонах, более
эффективны, чем другие. Например, символьный класс [aeiou]
работает гораздо эффективнее набора альтернатив (a|e|i|o|u).
Как правило, более простая конструкция наболее эффективна.
Книга Джеффри Фридла содержит много обсуждений вопроса оптимизации
регулярных выражений.
</para>
<para>
Если шаблон начинается с .* и включили флаг
<link linkend="reference.pcre.pattern.modifiers">PCRE_DOTALL</link>,
шаблон неявно заякоривается, поскольку шаблон может совпадать только
в начале строки. Но если модификатор
<link linkend="reference.pcre.pattern.modifiers">PCRE_DOTALL</link>
не включили, PCRE не может выполнить соответствующую оптимизацию,
поскольку в этой ситуации метасимвол «.» не соответствует символу начала
строки (если обрабатываемые данные содержат переводы строк, такой шаблон
может соответствовать шаблону не от начала строки, а от позиции
непосредственно после перевода строки).
Например, применяя шаблон
<literal>(.*) second</literal>
к строке «first\nand second» (где \n — символ перевода строки), значение,
которое захватил первый подшаблон, будет «and».
Чтобы обработать все возможные точки соответствия, модуль PCRE пытается
сопоставить шаблон после каждого символа перевода строки.
</para>
<para>
При работе с подобными шаблонами и обработке
данных без переводов строк для лучшей производительности
включают модификатор
<link linkend="reference.pcre.pattern.modifiers">PCRE_DOTALL</link>,
либо начинают шаблон с циркумфлекса ^.* для явного заякоривания.
Это защитит модуль PCRE от поиска символов новых строк и дополнительных
попыток сопоставить шаблон с каждой такой найденной позицией.
</para>
<para>
Избегайте шаблонов, которые содержат вложенные неограниченные повторения.
Сопоставление таких шаблонов со строками, которые не содержат совпадений,
занимает длительное время. Рассмотрим пример шаблона
<literal>(a+)*</literal>
</para>
<para>
Он может соответствовать значению «aaaa» тридцатью тремя различными способами,
и эта цифра очень быстро растёт при увеличении строки. (В примере
квантификатор * может совпадать 0, 1, 2, 3 или 4 раза,
и для каждого такого случая, кроме нуля, квантификатор + также может
совпадать различное число раз.) Если остаток шаблона таков, что все
совпадение терпит неудачу, модуль PCRE должен попробовать все возможные
варианты совпадения, что может потребовать огромного количества времени.
</para>
<para>
За счёт оптимизации отлавливают наиболее простые случаи наподобие
<literal>(a+)*b</literal>
где следом идёт литеральный символ. Перед началом стандартной
процедуры поиска модуль PCRE проверяет в следующей подстроке наличие
символа «b», и если символа нет, попытка сопоставления
немедленно завершается неудачей. Однако, когда последующего литерала нет,
оптимизация не может быть применена. Разница заметна при сравнении поведения шаблона
<literal>(a+)*\d</literal>
с поведением приведённого выше шаблона. Первый определяет
невозможность сопоставления практически сразу, при сопоставлении
со строкой, которая состоит из символов «a», тогда как второй
тратит длительное время на поиск в строках с длиной больше 20 символов.
</para>
</section>
</chapter>
<!-- Keep this comment at the end of the file
Local variables:
mode: sgml
sgml-omittag:t
sgml-shorttag:t
sgml-minimize-attributes:nil
sgml-always-quote-attributes:t
sgml-indent-step:1
sgml-indent-data:t
indent-tabs-mode:nil
sgml-parent-document:nil
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
sgml-exposed-tags:nil
sgml-local-catalogs:nil
sgml-local-ecat-files:nil
End:
vim600: syn=xml fen fdm=syntax fdl=2 si
vim: et tw=78 syn=sgml
vi: ts=1 sw=1
-->