≪
≫

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

В данной лекции выясняются механизмы устранения зацикливаний при использовании квантификаторов *,+,{}, которые применяются к шаблону, соответствующему пустому фрагменту текста; при использовании модификатора g и совпадении с пустым фрагментом текста. Рассматривается смысл и назначение якоря G, использование этого якоря, назначение модификатора с, итеративный поиск с применением якоря G и модификаторов g и gc, а также создание программ лексического анализа текста с помощью модификаторов gc.

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

Предотвращение зацикливания при поиске и замене

Версия 8 регулярных выражений системы программирования Perl, которую (версию) мы изучаем, дает очень мощные средства для поиска и замены образцов текста. Но за этой мощью кроются сложности ее применения. Сейчас мы рассмотрим один сложный аспект применения регулярных выражений Perl. Как вы уже знаете, совпадение может быть не только с фрагментом текста, но также и с позицией в тексте, а при замене, когда не было совпавшего текста, а была найдена только позиция совпадения, заменяющий текст подставляется в эту' позицию. За этим кроется возможность зацикливания при поиске в цикле или с модификатором#. Рассмотрим такой пример:

while (‘abed' =” /z?/) { ... }

Ясно, что этот цикл будет выполняться вечно, т.к. без модификатора g не будет смешения текущей позиции поиска. Но здесь это нормально, так задумал программист.

Однако бесконечный цикл можно организовать также и внутри регулярного выражения, применяя квантификатор к скобкам, которые захватили пустой фрагмент текста:

$_=’abcd’;

print if /(z?)*/;

Мы печатаем все до совпадения, за ним вертикальную черту7 и далее все после совпадения. Будет напечатано

| abed

Модификатор * заставляет совпадать z, взятое 0 раз, еще и еще с того же самого начала текста. Чтобы предотвратить подобное зацикливание внутри системы поиска совпадения, эта система прерывает такой цикл, обусловленный повтором совпадения с нулевой длиной.

Бесконечный цикл также можно задать во внешнем цикле выполнения поиска и замены, с помощью модификатора g:

$_=’abed’;

s/.??//g;

print

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

<><><>o<>

Это означает, что была найдена и заменена каждая буква, и кроме того, найденный фрагмент $& вставлен в каждую позицию между буквами, а также в начало и конец текста. А казалось бы. замена должна была бы выполняться бесконечно путем вставки $& в начало текста. Эта проблема решена разработчиками регулярных выражений Perl так: переменной, которая является операндом оператора поиска/замены, присваивается дополнительное состояние «предыдущее совпадение имело нулевую длину». Это состояние хранится в переменной лишь между циклами поиска, обусловленными модификатором g, и сбрасывается явным или неявным присвоением значения функции pos для этой переменной. Если в каком-то цикле под модификатором g получается совпадение нулевой длины и данная переменная имеет установленное состояние «предыдущее совпадение было нулевой длины», то в конце такого цикла система поиска совпадения производит принудительный поиск с возвратами, пока не произойдет совпадения с непустым фрагментом текста.

В связи с этим материалом обратите внимание еще на такой парадоксальный пример:

print |$&|$’п» it labc’ =" /(а?)*/;

print length $1;

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

|a|bc

О

Текущий фрагмент совпадения М равен а, а в $1 захвачен пустой фрагмент текста. Как будто бы это ошибка, но на самом деле ошибки в этом примере нет. Вначале квантификатор ? получает значение 7, квантификатор * тоже получает значение 7. Происходит совпадение скобок с буквой а. Затем квантификатор * заставит подшаблон в скобках сделать повтор с позиции 7 (за буквой а), и в результате будет найдено совпадение нулевой длины для а с квантификатором ?, равным нулю. При этом переменная 57 обновится и получит пустое значение. В следующем повторе зафиксируется нулевая длина совпадения, и выполнение оператора поиска принудительно прервется. Если переписать этот пример в виде

print |$&|$’п» if labc’ =“ /(?:(а?)(?{print 1pos=’.pos($_).", text=’$i’ ”}))*/;

print length $1;

и посмотреть, что будет напечатано:

pos=1, text='a’

pos=1, text=’'

|a|bc

О

то алгоритм работы системы поиска в этом примере станет ясен. Это немного напоминает уже рассмотренный пример

$_=':abc:’;

print "$& $1» if /(w)+/;

где вместо квантификатора * стоит квантификатор +. Этот пример напечатает

abc с

 
Посмотреть оригинал