Регулярные выражения Perl и их применение

       

Опережающая проверка


Это сложное условие (мнимый символ, якорь). Эту проверку еще называют "заглядыванием вперед". Вводится она конструкцией

(?= шаблон )

Шаблон может быть как угодно сложным. Это условие истинно, если в текущей позиции находится текст, совпадающий с заданным шаблоном. Обратите внимание, что эти условные конструкции совпадают не с текстом, а с позицией в тексте подобно своим более простым собратьям \b, \A, $ и т.д.

Замечу, что атомарная группировка имитируется позитивной опережающей проверкой и обратной ссылкой:

(?> шаблон ) эквивалентно (?=( шаблон ))\1

Происходит это потому, что после выполнения опережающей (как и ретроспективной) проверки уничтожаются все сохраненные состояния, возникшие внутри нее.

Имеется противоположный случай проверки - негативная опережающая проверка:

(?! шаблон )

Такое условие истинно, если в текущей позиции нет текста, совпадающего с заданным шаблоном.

Например, шаблон

\w+(?=\s+)

найдет слово, за которым стоит один или несколько пробельных символов, но сами пробельные символы в результат поиска не войдут. Дело в том, что позиционная проверка не поглощает текст: после применения этих условных конструкций текущая позиция в тексте вернется на прежнее место, где была до применения этого якоря. Интересно, что захватывающие скобки внутри этих проверок захватывают текст, о чем забывают упомянуть авторы книг по Perl.

Рассмотрим такой пример:

$_='abc'; print "1\n" if /a(?=(b))bc/; print $1;

Будут напечатаны такие строки:

1 b

Как видим, после совпадения с символом a после него ищется символ b. Он находится, и поиск продолжается дальше. Если сразу бы после a не стоял символ b, то поиск потерпел бы неудачу и началась бы следующая итерация, т.к. сохраненных состояний, к которым можно вернуться, нет. Внутри опережающей проверки этот символ захватывается в переменную $1, после чего проверка заканчивается успешно, и текущая позиция в строке возвращается к символу b. Затем выполняется соответствие подшаблона bc символам bc, на этом оператор поиска успешно завершается.

Подобные сложные проверки бывают необходимы в сложных случаях, когда надо принять решение в зависимости от того, какой фрагмент текста находится в текущей позиции (или вообще впереди нее). Эти условия обычно используются в условных конструкциях, о которых речь впереди.

Обратите внимание на интересную особенность сложных условий: в результате работы оператора

"a" =~ /(?=(a))/

переменная $1 получит значение a, а в результате работы похожего регулярного выражения

"a" =~ /((?=a))/

переменная $1 получит пустое значение. Это можно объяснить тем, что в первом случае внутри сложного условия происходит захват буквы a, а затем после выхода за это сложное условие (на конец регулярного выражения) текущая позиция в шаблоне возвращается в точку перед буквой a, но сама буква уже сидит в переменной $1. Во втором случае после выполнения фрагмента шаблона ((?=a текущая позиция в шаблоне передвигается за букву a, но после закрытия скобки в сложном условии ((?=a) происходит возврат текущей позиции в шаблоне перед буквой а, и после закрытия захватывающей скобки эта текущая позиция остается перед буквой a, как и в момент открытия захватывающей пары скобок. В результата эта пара скобок захватывает пустой фрагмент текста.



Содержание раздела