Якорь G, его смысл и использование
Мнимый символ G означает позицию конца предыдущего совпадения. Это аналог функции pos, но только внутри регулярного выражения. Этот метасимвол впервые появился в Perl для проведения глобального поиска и замены. Он совпадает с позицией, в которой закончилась предыдущая итерация поиска с модификатором g. Свое значение этот якорь хранит также и после окончания работы оператора поиска/замены. как и функция pos. При первой итерации поиска/замены или после сброса текущей позиции поиска в начало текста G совпадает в начале текста, как и метасимвол Л. При принудительной переустановке текущей позиции поиска при обращении к функции pos этот якорь соответствует установленной позиции. Рассмотрим примеры, которые показывают, как работает якорь G и чем он может быть полезен.
Мы уже рассматривали примеры поиска в скалярном контексте с модификатором g. При обращению к другому или тому же оператору с модификатором g и той же переменной с целевым текстом отыскивается следующее совпадение. Но оно ищется без привязки к концу предыдущего совпадения и может быть найдено с любой позиции после конца предыдущего совпадения. Иногда бывает нужно, чтобы следующий поиск был привязан к концу последнего совпадения и чтобы поиск заканчивался неудачей, если поиск следующего образца начинается не с этой начальной позиции. Это аналогично привязке к началу текста Л.
Например, мы ищем слова, разделенные вертикальной чертой:
$_=’|ab|cd |ef';
while (/|(w+)/g) { print “$1 ” }
Этот пример выведет
ab
cd
ef
А теперь потребуем, чтобы слова были разделены лишь одной вертикальной чертой. Вот здесь и нужна эта привязка G к концу предыдущего совпадения:
$_=’|ab|cd |ef;
while (/G|(w+)/g) { print u$1 " }
В результате получаем ab
cd
Якорь 6’ надежно работает, если он стоит в самом начале регулярного выражения, которое не имеет высокоуровневой конструкции выбора альтернативы. Если такая конструкция имеется, то якорь надо выносить за скобки:
/G(?: шаблоні | шаблон2 | ... )/
Если этот мнимый символ стоит где-либо в другом месте, то правильная его работа не гарантируется, во всяком случае, это приведет к сильному замедлению в работе регулярного выражения.
Теперь поясню, чем же отличается конец предыдущего совпадения от начала текущего совпадения. Неужели это не одно и то же? Если совпадение было с непустым фрагментом текста, то это то же самое. Но если совпадение было с «пустотой», то. как мы уже знаем, механизм регулярных выражений принудительно передвигает текущую позицию поиска на один символ, чтобы избежать зацикливания. Рассмотрим такие примеры:
$_-'abcd’;
s/z*/!/g;
print $_;
Восклицание будет вставлено в каждую позицию строки: ! a! b! с! d!
Теперь поставим в начало регулярного выражения якорь G:
$_='abcd’;
s/Gz*/f/g;
print $_;
Восклицание будет вставлено только в начало строки:
!abcd
После первой итерации замены текущая позиция поиска продвинется на один символ и станет равна единице, а конец предыдущего совпадения будет равен нулю (в исходной строке). Поэтому поиск во второй итерации закончится неудачей, и замены на этом прекратятся.
Заметим еще, что если комбинируется поиск в скалярном и списковом контексте, то якорь G хранит свое значение только после успешного поиска в скалярном контексте с модификатором g. После поиска в списковом контексте с модификатором g якорь С7 сбрасывается в начало текста.
Рассмотрим такие примеры. Сначала ищем в скалярном, а затем в списковом контексте:
$_=’abcd’;
/w/g;
my @a=/w/g;
print @a;
Будет напечатано
bed
Символ а был пройден при первом поиске.
Теперь поменяем операторы:
$_=’abcd’;
my @a=/w/g;
print “@a ”;
if (/(w)/g) { print “Found $1“ }
Напечатается
abed
Found a
Это можно объяснить тем, что поиск в списковом контексте продо-жается до своей неудачи, а она сбрасывает позицию поиска и якорь G в начало текста. То же самое сделает и поиск в скалярном контексте, если он будет выполняться в цикле до исчерпания совпадений:
$_=’abcd’;
while (/(w)/g) { print “$1” }
my @a-/w/g:
print “ @a”;
Будет напечатано: abed
abed
Запомните, что оператор поиска/замены, который хочет искать от конца предыдущего совпадения 6, обязательно должен иметь модификатор