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


         

Замена n-го совпадения - часть 2


Верно! Заменился номер телефона, который стоит нулевым справа. Далее даем $n значение 1 и смотрим результат… Вот неожиданность: заменился первый телефон

Тел. 9-9999. Другой тел. 3-2233, а вот еще один тел. 4-1122

а должен был бы тот, что посередине! При $n=2 - та же картина… Видимо, где-то закралась ошибка. Можете вы найти (и исправить) ее самостоятельно? Тогда читайте дальше.

Разберем работу этого регулярного выражения при $n=1. После того, как нашелся первый номер телефона по подшаблону (тел\.\s+)[\d-]+, началось заглядывание вперед, и подшаблон (?:.*?$tel){$n} поглотил второй телефонный номер. За ним началась проверка (?!.*?$tel), которая закончилась неудачей, т.к. после второго телефонного номера есть еще номер. "Не беда! - говорит механизм поиска соответствия. - Буду увеличивать значение минимального квантификатора .*? в подшаблоне (?:.*?$tel){$n}, авось поможет." И начинает его увеличивать и пробовать эти значения. Когда этот квантификатор захватит все до вертикальной черты в следу ющей строке:

Тел. 2-3344. Другой тел. 3-2233, а вот еще один т|ел. 4-1122

весь подшаблон захватит фрагмент

, а вот еще один тел. 4-1122

т.е. все до конца текста, и после этого проверка (?!.*?$tel) пройдет успешно. Налицо совпадение всего регулярного выражения, поэтому первый номер телефона будет заменен на 9-9999. Ошибка ясна: .*? начал поглощать символы, которые относились к номеру телефона, а он не должен был этого делать. Надо заменить его на правильный подшаблон - он должен поглощать символы до фрагмента тел., за которым идет номер.

Когда речь шла о пропуске символов до закрывающей угловой скобки при поиске ссылки, то все было просто: [^>]*, но здесь уже не один символ, поэтому конструкция [^$tel]*, вообще говоря, не годится, у нас не просто множество символов, а часть текста. Вот практический прием пропуска символов до данного фрагмента текста:

(?:(?!$tel).)*$tel

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

Вся программа теперь выглядит так:

#!/usr/bin/perl -w use strict; use locale;




Содержание  Назад  Вперед