Рекурсивные шаблоны
Рассмотрим проблему сопоставления строки в круглых скобках, которая допускает
неограниченное количество вложенных круглых скобок.
Лучшее, что можно сделать без рекурсии, — написать шаблон, который
будет решать задачу для ограниченной глубины вложенности, поскольку обработать
неограниченную глубину невозможно.
Perl 5.6 предоставил ряд экспериментальных возможностей,
которые в том числе разрешают реализовать рекурсию в шаблонах.
Специальное обозначение (?R) задают, чтобы указать рекурсивный
подшаблон. Таким образом, приведём PCRE-шаблон, который решает поставленную
задачу (подразумевается, что включён модификатор
PCRE_EXTENDED,
незначащие пробелы игнорируются):
\( ( (?>[^()]+) | (?R) )* \)
Вначале шаблон соответствует открывающей круглой скобке. Затем
шаблон соответствует любому количеству подстрок, каждая из которых
может быть последовательностью символов, кроме скобок, либо строкой, рекурсивно
соответствующей шаблону (т. е. строкой, корректно заключённой в
круглые скобки). И, в конце, идёт закрывающая круглая скобка.
Приведённый пример шаблона включает вложенные неограниченные повторения,
поэтому однократные шаблоны значительно ускоряют процесс
сопоставления, особенно когда строка не соответствует заданной
маске. Например, если применить этот шаблон к строке:
(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa()
,
то несоответствие будет обнаружено достаточно быстро. Но без
однократных шаблонов сопоставление будет затягиваться
на длительное время, поскольку существует множество способов разделения
строки между квантификаторами + и *, и все они должны быть проверены,
прежде чем будет выдано сообщение о неудаче.
Значение, которое устанавливают для захватывающего подшаблона, будет соответствовать
значению, захваченному на наиболее глубоком уровне рекурсии. Когда приведённый шаблон
сопоставляется со строкой
(ab(cd)ef)
,
захваченным значением будет «ef» — последнее значение,
принимаемое на верхнем уровне. Если добавить дополнительные скобки
\( ( ( (?>[^()]+) | (?R) )* ) \)
,
захваченным значением будет «ab(cd)ef». Если
в шаблоне встречается более 15 захватывающих скобок, модулю PCRE
требуется больше памяти для обработки рекурсии, чем обычно.
Память выделяется функцией pcre_malloc и освобождается
функцией pcre_free. Если функция не может выделить память,
сохраняются данные только для первых 15 захватывающих скобок,
поскольку нет способа выдать ошибку out-of-memory изнутри рекурсии.
Конструкции (?1)
, (?2)
и так далее
указывают для рекурсивных подшаблонов.
Допустимо также указывать именованные подшаблоны:
(?P>name)
или (?&name)
.
Если синтаксис ссылки на рекурсивный подшаблон (как по имени, так и по
числовому индексу) указали вне скобок, к которым он относится,
парсер обработает подшаблон аналогично подпрограмме в языке программирования.
Возьмём более ранний пример, который указывает, что шаблон
(sens|respons)e and \1ibility
соответствует значениям «sense and sensibility» и «response and responsibility»,
но не «sense and responsibility». Если вместо этого написать шаблон
(sens|respons)e and (?1)ibility
,
он совпадёт со значением «sense and responsibility» так же, как и с другими двумя
строками. Однако такие ссылки должны быть указаны после подшаблона, на который
они ссылаются.
Максимальная длина входной строки — наибольшее положительное число,
которое может содержать целочисленная переменная. Однако, поскольку для обработки
подшаблонов и бесконечного повторения модуль PCRE запускает рекурсию, это означает,
что размер обрабатываемых строк в некоторых шаблонах также может быть
ограничен доступным размером стека.