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


Атомарная группировка - часть 2


и строку

abc

Совпадение будет найдено, но только при третьей итерации. \w* захватит 0 символов, а литерал c совпадет с символом c. Если в "жадном" режиме квантификатор внутри атомарной группировки захватывает все и не отдает, то в минимальном режиме он берет минимум возможного и ничего больше.

Если бы мы вынесли квантификатор за пределы атомарной группировки:

(?>\w)*c (?>\w)*?c

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

Вот более практический и сложный пример: пусть нам надо округлять числа типа

23.34000012 23.345000023 34.4000025 456.00

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

23.34 23.345 34.40 456.00

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

Вначале нас интересует десятичная точка: \., затем - две обязательных цифры: \d\d. За ними может идти цифра от 1 до 9, а может и не идти: [1-9]?. Все это мы возьмем в захватывающие скобки, чтобы этим заменить все от точки до конца числа. А после еще могут идти цифры: \d*, они будут удалены. Оператор получается такой:

s/(\.\d\d[1-9]?)\d*/$1/;

Но этот оператор делает замену, к примеру, в числе 23.345, меняя найденное на себя, т.е. выполняя ненужную работу. Вот пример кода:

$_='23.345'; print s/(\.\d\d[1-9]?)\d*/$1/."\n"; print $_;

Он выведет

1 23.345

Напомню, что оператор s возвращает число успешно сделанных замен. Вот, как это происходит: подшаблон

(\.\d\d[1-9]?)

совпадает с

.345

Далее цифр нет, поэтому \d* совпадает с пустой подстрокой, и т.к. совпадение найдено, то замена срабатывает. А она должна срабатывать только, если после .345 есть цифра. Давайте для этого попробуем заменить \d* на \d+ и посмотрим, что получится. А получается результат

1 23.34




Начало  Назад  Вперед



Книжный магазин