Опубликован: 18.06.2007 | Доступ: свободный | Студентов: 1354 / 35 | Оценка: 4.14 / 3.29 | Длительность: 12:44:00
ISBN: 978-5-94774-604-4
Лекция 6:

Предотвращение зацикливания при поиске и замене. Якорь \G, итеративный поиск с модификаторами g и gc

< Лекция 5 || Лекция 6: 1234 || Лекция 7 >

6.2.1.Запрет сброса позиции \G модификатором c

Мы можем запретить сброс позиции \G в начало текста, если совпадение не было найдено.

Для этого имеется модификатор c. Он не применяется без модификатора g, поэтому gc является идиомой, как бы одним модификатором gc, хотя модификаторы могут записываться в любом порядке и даже повторяться, что не влечет каких-либо последствий (за исключением повтора модификатора e в операторе подстановки s///).

Вот как выглядит последний пример с использованием модификатора c:

$_='abcd';
while (/(\w)/gc) { print "$1 " }
my @a=/\w/g;
print "\n".scalar @a;

Напечатается следующее:

a b c d
0

Мы видим, что второй поиск завершился неудачей, и размер массива @a равен нулю.

Аналогично работает и предпоследний пример:

$_='abcd';
my @a=/\w/gc;
print "@a\n";
if (/\w/g) { print "Found $1" } else { print 'Not found' }

Будет напечатано:

a b c d
Not found

Если бы мы распечатали значение функции pos($_) после исчерпывающего поиска с модификатором gc, то pos указывала бы в конец строки (в нашем случае это 4). После исчерпывающего поиска с одним модификатором g, когда \G сбрасывается в начало текста, значение pos($_) становится неопределенным, как и для любой переменной, для которой не выполнялся поиск с модификатором g, или после присвоения этой переменной значения.

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

Полезным свойством функции 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.

< Лекция 5 || Лекция 6: 1234 || Лекция 7 >
Константин Бражников
Константин Бражников
Россия
Mike .
Mike .
Россия