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

Предварительная настройка начальной позиции поиска


Полезным свойством функции pos является то, что ей можно присваивать значение!

Например, вы знаете, что в переменной $s совпадение будет найдено только после тысячного символа. Вы можете начать поиск именно с этого символа, присвоив

pos($s)=1000;

Напомню, что счет символов идет с нуля. Но оператор поиска для использования этого значения должен иметь модификатор g (или gc). Вот пример:

$_='abcd'; pos($_)=1; /\w/g; print "$&\n"; pos($_)=1; print /\w/g;

На печати получим:

b bcd

Второй раз оператор поиска был использован в списковом контексте, потому что оператор print ожидает список значений.

Якорь \G учитывает значение, присвоенное функции pos, что видно на следующих примерах:

$_='abcd'; pos($_)=1; /\G\w/g; print "$&\n"; pos($_)=1; print /\G\w/g;

Результат такой же:

b bcd

А теперь рассмотрим более сложный пример, когда без якоря \G трудно организовать поиск. Предположим, данные представляют собой сплошную последовательность черырехзначных чисел без разделителей, и нам нужно отобрать из нее все числа, которые начинаются на 99.

Прямая попытка

my @a=/99\d\d/g

ведет к неудаче, потому что после несовпадения текущая позиция поиска будет увеличена на один символ и следующие итерации поиска не будут начинаться с начала чисел. В примере

$_='12991234'; my @a=/99\d\d/g; print @a;

будет "найдено" число 9912.

Попробуем вариант с минимальным квантификатором, который пропускает все четверки чисел до числа 99\d\d, а сохраняющие скобки захватывают в массив @a нужные числа:

$_='12991234'; my @a=/(?:\d{4})*?(99\d\d)/g; print @a;

Опять "найдено" число 9912… В этом примере при отсутствии совпадения (когда в тексте не останется нужных чисел) также применяется механизм смещения на один символ, и поиск продолжается не с границы чисел. Аналогично ведет себя негативная опережающая проверка с максимальным квантификатором:

$_='12991234'; my @a=/(?:(?!99)\d{4})*(99\d\d)/g; print @a;

Опять 9912… Первая пара скобок всегда заканчивается на границе числа, но при несовпадении второй пары будет смещение текущей позиции поиска, что все и портит.

Можно было бы ко второй паре скобок поставит квантификатор ?, чтобы при несовпадении со следующим числом эти захватывающие скобки совпали с пустым фрагментом, и это не привело бы к смещению текущей позиции поиска на один символ.

Но тогда в массиве @a появилось бы много элементов с неопределенным значением, потому что при каждой итерации поиска формировался бы очередной элемент массива независимо от того, найдено число или нет, - ведь теперь все регулярное выражение может совпасть с пустым фрагментом, а совпадение для захватывающих скобок вообще не обязательно.

Наиболее четкое и простое решение дает использование якоря \G. Он разрешает поиск только от конца предыдущего числа, не допуская увеличения текущей позиции поиска на один символ при несовпадении:

$_='12991234'; my @a=/\G(?:(?!99)\d{4})*(99\d\d)/g; print @a;

В этом примере поиск закончится неудачей и возвратится пустой список. В примере

$_='129912349934'; my @a=/\G(?:(?!99)\d{4})*(99\d\d)/g; print @a;

напечатается число 9934.



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