mirror of
https://github.com/php/doc-ru.git
synced 2026-03-23 23:32:16 +01:00
2386 lines
142 KiB
XML
2386 lines
142 KiB
XML
<?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><></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>(?>\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>символы с кодами 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.
|
||
Начиная с 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<name>pattern)</literal>. Этот подшаблон будет индексирован
|
||
в массиве совпадений кроме обычного числового индекса, ещё и по имени name.
|
||
Именованные подшаблоны записывают двумя альтернативными синтаксисами:
|
||
<literal>(?<name>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<name></literal>, <literal>\k'name'</literal>, <literal>\k{name}</literal>,
|
||
<literal>\g{name}</literal>, <literal>\g<name></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>
|
||
начинаются с (?<= для положительных утверждений и (?<!
|
||
для отрицающих. Например,
|
||
|
||
<literal>(?<!foo)bar</literal>
|
||
|
||
найдёт вхождения «bar», которым не предшествует «foo». Сами
|
||
утверждения «назад» ограничены так, чтобы все подстроки, которым
|
||
они соответствуют, имели фиксированную длину. Но, Если
|
||
используются несколько альтернатив, они не обязаны иметь одинаковую
|
||
длину. Таким образом шаблон
|
||
|
||
<literal>(?<=bullock|donkey)</literal>
|
||
|
||
корректен, но
|
||
|
||
<literal>(?<!dogs?|cats?)</literal>
|
||
|
||
вызовет ошибку во время компиляции. Ветки, которые соответствуют
|
||
строкам разной длины, разрешены только на верхнем уровне утверждений
|
||
касательно предшествующего текста. Это расширение относительно
|
||
Perl 5.005, который требует чтобы все ветки соответствовали строкам
|
||
одинаковой длины. Такое утверждение как
|
||
|
||
<literal>(?<=ab(c|de))</literal>
|
||
|
||
не корректно, поскольку верхний уровень маски может соответствовать
|
||
строкам разной длины, но его можно преобразовать к корректному шаблону,
|
||
используя альтернативы на верхнем уровне:
|
||
|
||
<literal>(?<=abc|abde)</literal>
|
||
|
||
Утверждения касательно предшествующего текста реализованы так,
|
||
что для каждой альтернативы текущая позиция временно переносится
|
||
назад, на фиксированную ширину, после чего выполняется поиск
|
||
соответствия условию. Если перед текущей позицией недостаточно
|
||
символов, поиск соответствия терпит неудачу. Утверждения назад в сочетании
|
||
с однократными подшаблонами удобны для поиска
|
||
в конце строки; соответствующий пример приведён в конце раздела
|
||
«Однократные подшаблоны».
|
||
</para>
|
||
<para>
|
||
Несколько утверждений (разных типов) могут присутствовать в
|
||
утверждении, например:
|
||
|
||
<literal>(?<=\d{3})(?<!999)foo</literal>
|
||
|
||
совпадает с подстрокой «foo», которой предшествуют три цифры,
|
||
отличные от «999». Следует понимать, что каждое из утверждений
|
||
проверяется относительно одной и той же позиции в обрабатываемом
|
||
тексте. Вначале выполняется проверка того, что предшествующие три символа —
|
||
это цифры, затем проверяется, чтобы эти же цифры не являлись
|
||
числом 999. Приведённый выше шаблон не соответствует подстроке
|
||
«foo», которой предшествуют шесть символов, первые три из которых —
|
||
цифры, а последние три не образуют «999». Например, он не
|
||
соответствует строке «123abcfoo», в то время как шаблон
|
||
|
||
<literal>(?<=\d{3}...)(?<!999)foo</literal>
|
||
соответствует.
|
||
</para>
|
||
<para>
|
||
В этом случае анализируются предшествующие шесть
|
||
символов на предмет того, чтобы первые три из них были цифрами,
|
||
а последние три не образовали «999».
|
||
</para>
|
||
<para>
|
||
Утверждения могут быть вложенными, причём в произвольных сочетаниях:
|
||
|
||
<literal>(?<=(?<!foo)bar)baz</literal>
|
||
|
||
соответствует подстроке «baz», которой предшествует «bar»,
|
||
перед которой, в свою очередь, нет «foo», а
|
||
|
||
<literal>(?<=\d{3}...(?<!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». Записываются однократные шаблоны
|
||
при помощи круглых скобок следующим образом: (?>. Например:
|
||
|
||
<literal>(?>\d+)bar</literal>
|
||
</para>
|
||
<para>
|
||
Этот вид подшаблона предотвращает повторный анализ подшаблона, если
|
||
сопоставление последующих элементов терпит неудачу. Однако это не мешает
|
||
повторно анализировать любые другие элементы, в том числе те, которые предшествуют
|
||
однократному подшаблону.
|
||
</para>
|
||
<para>
|
||
Говоря другими словами, подшаблоны такого типа соответствуют
|
||
той части подстроки, которой соответствовала бы одиночная
|
||
изолированная маска, заякоренная на текущей позиции обрабатываемого
|
||
текста.
|
||
</para>
|
||
<para>
|
||
Однократные подшаблоны — незахватывающие. Простые примеры,
|
||
подобные приведённому, можно охарактеризовать как безусловный
|
||
захват максимального количества повторений. В то время как
|
||
\d+ и \d+? корректируются так, чтобы остальные части шаблона
|
||
так же совпали, (?>\d+) соответствует исключительно максимальной по
|
||
длине последовательности цифр, даже если это приводит к неудаче при
|
||
сопоставлении других частей шаблона.
|
||
</para>
|
||
<para>
|
||
Однократным подшаблонам разрешено включать в себя более сложные конструкции
|
||
и быть вложенными.
|
||
</para>
|
||
<para>
|
||
Однократные подшаблоны разрешено указывать совместно с утверждениями, которые
|
||
касаются предыдущего текста для описания эффективных сопоставлений
|
||
в конце обрабатываемого текста. Рассмотрим простой шаблон
|
||
|
||
<literal>abcd$</literal>
|
||
|
||
в применении к длинному тексту, который не соответствует указанной маске.
|
||
Поскольку поиск происходит слева направо, вначале PCRE будет
|
||
искать букву «a», и только потом анализировать следующие
|
||
записи в шаблоне. Если шаблон указан в виде
|
||
|
||
<literal>^.*abcd$</literal>.
|
||
|
||
В этой ситуации вначале .* сопоставляется со всей строкой, после
|
||
чего сопоставление терпит неудачу (так как нет последующего символа «a»).
|
||
После чего .* сопоставляется со всей строкой, кроме последнего символа,
|
||
потом кроме двух последних символов, и так далее. В конечном итоге
|
||
поиск символа «a» происходит по всей строке. Однако, если шаблон записать
|
||
в виде:
|
||
|
||
<literal>^(?>.*)(?<=abcd)</literal>
|
||
|
||
повторный анализ для .* не выполняется, и, как следствие, может
|
||
соответствовать только всей строке целиком. После чего утверждение
|
||
проверяет последние четыре символа на совпадение с «abcd», и в случае
|
||
неудачи все сопоставление терпит неудачу. Для больших объёмов
|
||
обрабатываемого текста этот подход имеет значительный выигрыш во времени
|
||
выполнения.
|
||
</para>
|
||
<para>
|
||
Если шаблон содержит неограниченное повторение внутри подшаблона,
|
||
который в свою очередь также может повторяться неограниченное количество
|
||
раз, однократные подшаблоны помогают
|
||
избегать многократных неудачных сопоставлений,
|
||
которые длятся достаточно продолжительное время. Шаблон
|
||
|
||
<literal>(\D+|<\d+>)*[!?]</literal>
|
||
|
||
соответствует неограниченному количеству подстрок, которые состоят
|
||
не из цифр, либо из цифр заключённых в <>, за которыми следует
|
||
? либо !. Если в обрабатываемом тексте содержатся
|
||
соответствия, время работы регулярного выражения будет невелико.
|
||
Но если его применить к строке
|
||
|
||
<literal>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</literal>
|
||
|
||
это займёт длительное время. Это связанно с тем, что строка
|
||
может быть разделена между двумя частями шаблона многими способами,
|
||
и все они будут опробованы (в примере мы использовали [?!], поскольку
|
||
в случае одиночного символа в конце шаблона и PCRE и Perl выполняют
|
||
оптимизацию. Они запоминают последний одиночный символ и в случае
|
||
его отсутствия выдают неудачу). Если изменить шаблон на
|
||
|
||
<literal>((?>\D+)|<\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>name)</literal> или <literal>(?&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
|
||
-->
|