1
0
mirror of https://github.com/php/doc-ru.git synced 2026-04-28 09:43:16 +02:00
Files
archived-doc-ru/reference/pcre/pattern.syntax.xml
T
2021-01-03 21:01:46 +03:00

2315 lines
140 KiB
XML
Raw 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: d65981e8ecf8def27d75b1899dfd1dfff7d29277 Maintainer: shein Status: ready -->
<!-- Reviewed: yes -->
<!-- $Revision$ -->
<!-- splitted from ./en/functions/pcre.xml, last change in rev 1.2 -->
<chapter xml:id="reference.pcre.pattern.syntax" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Синтаксис регулярных выражений</title>
<titleabbrev>Описание синтаксиса Perl-совместимых регулярных выражений</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>расширяет смысл метасимвола (, является также квантификатором, означающим 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>
Второе применение обратного слеша заключается в том, что он позволяет использовать
непечатные символы в видимой форме в описании шаблона.
При том, что в PCRE нет ограничений на использование непечатных символов
(исключая бинарный ноль, который интерпретируется как конец шаблона),
при редактировании программного кода в каком-либо текстовом редакторе
гораздо удобнее использовать следующие комбинации, чем реальные символы, которые
они представляют:
</para>
<para>
<variablelist>
<varlistentry>
<term><emphasis>\a</emphasis></term>
<listitem>
<simpara>символ оповещения, сигнал, (BEL, шестнадцатеричный код 07)</simpara>
</listitem>
</varlistentry>
<varlistentry>
<term><emphasis>\cx</emphasis></term>
<listitem>
<simpara>"Ctrl+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 character с кодом,
совпадающим с данным шестнадцатеричным числом.
Исходная шестнадцатеричная экранирующая последовательность,
<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>любой горизонтальный пробельный символ (начиная с версии PHP 5.2.4)</simpara></listitem>
</varlistentry>
<varlistentry>
<term><emphasis>\H</emphasis></term>
<listitem><simpara>любой символ, не являющийся горизонтальным пробельным символом (начиная с версии PHP 5.2.4)</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>любой вертикальный пробельный символ (начиная с версии PHP 5.2.4)</simpara></listitem>
</varlistentry>
<varlistentry>
<term><emphasis>\V</emphasis></term>
<listitem><simpara>любой символ, не являющийся вертикальным пробельным символом (начиная с версии PHP 5.2.4)</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>
и якорь в конце строки.
</para>
<para>
Последовательность <literal>\K</literal> может быть использована для сброса
начала совпадения начиная с версии PHP 5.2.4. Например, шаблон
<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>
Начиная с версии 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>
Расширенные свойства, такие как музыкальные символы
(<literal>InMusicalSymbols</literal>) не поддерживаются в PCRE.
</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", любым алфавитным символом или "%". Поддерживаются следующие имена
классов:
<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>символы с кодами 0 - 127</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.
Начиная с PHP 5.3.0 и 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>
</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>
Указывает на необходимость захвата подстроки (как показано выше).
В том случае, если соответствие шаблону было найдено, подстрока,
соответствующая подмаске, также передается обратно вызывающему
при помощи аргумента <emphasis>ovector</emphasis> функции
<function>pcre_exec</function>. Открывающие круглые скобки нумеруются
слева направо (начиная с единицы) и их порядковые номера
используются для нумерации соответствующих подстрок в результате.
</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 соответственно. Максимальное количество захватывающих подмасок - 65535.
Такие большие шаблоны могут не скомпилироваться, в зависимости от настроек 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.
В PHP 5.2.2 было добавлено два альтернативных синтаксиса:
<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>Sat</literal>, то она будет
помещена в ссылку 1, а ссылка 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>
Общий квантификатор повторения указывает минимальное и максимальное
допустимое количество совпадений, согласно двум числам, заключенными
в фигурные скобки и разделенными запятой. Числа должны быть меньше чем 65536,
и первое число не должно превышать второе по значению.
Например:
<literal>z{2,4}</literal>
соответствует "zz", "zzz" или "zzzz". Закрывающая фигурная скобка сама
по себе не является специальным символом. В случае, если второе число опущено,
но запятая присутствует, нет верхнего предела; в случае, если и второе
число и запятая опущены, требуется точное число повторений.
Таким образом
<literal>[aeiou]{3,}</literal>
соответствует как минимум трем последовательным гласным (а также любому
их количеству выше трех), в то время как
<literal>\d{8}</literal>
соответствует ровно восьми цифрам. Открывающая фигурная скобка,
расположенная в недопустимой для квантификатора позиции, либо не
соответствующая синтаксису квантификатора, интерпретируется как
обыкновенная символьная строка. Например, {,6} не является квантификатором,
а интерпретируется как символьная строка из четырех символов.
</para>
<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>
корректно находит все комментарии языка Си. Использование символа '?' после
квантификатора влияет исключительно на его жадность, и не затрагивает
никакие другие свойства. Не следует путать использование символа '?'
как, собственно, квантификатора (ноль либо одно соответствие) и как ограничителя
жадности. Также в следствие его двойственной функциональности может
использоваться следующая запись:
<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>
Вне символьного класса обратный слеш с последующей цифрой
больше нуля (и, возможно, последующими цифрами) интерпретируется
как ссылка на предшествующую захватывающую подмаску, предполагая,
что соответствующее количество предшествующих открывающих круглых
скобок присутствует.
</para>
<para>
Однако, в случае, если следующее за обратным слешем число меньше 10,
оно всегда интерпретируется как обратная ссылка, и приводит к ошибке
только в том случае, если нет соответствующего числа открывающих
скобок. Другими словами, открывающие скобки не обязаны предшествовать
ссылке для чисел меньше 10. "Упреждающая обратная ссылка" может
иметь смысл, если используется повторение и более поздняя подмаска
участвует в ранней итерации. Более детальную информацию об
обработке цифр после обратного слеша можно найти в разделе
"Обратный слеш".
</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>
Начиная с PHP 5.2.2, управляющая последовательность <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> или, начиная с PHP 5.2.2,
<literal>\k&lt;name&gt;</literal> или <literal>\k'name'</literal>.
Кроме того, в PHP 5.2.4 была добавлена поддержка <literal>\k{name}</literal>
и <literal>\g{name}</literal>, а в PHP 5.2.7 для
<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">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>
</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" 33-мя различными способами, и эта
цифра очень быстро растет при увеличении строки. (В данном примере,
квантификатор * может совпадать 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
-->