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

Динамические регулярные выражения

< Лекция 11 || Лекция 12: 12345 || Лекция 13 >

В документации по Perl приводится пример применения динамических регулярных выражений для того, чтобы выяснить, является ли заданная в тексте последовательность чисел последовательностью Фибоначчи. А мы в качестве более сложного примера поставим немного другую задачу: найти в $_ первую наидлиннейшую последовательность 10-ных цифр, которые стоят подряд и возрастают на 1. Например, в строке

$_='0123 1234345678910 ';

искомая наидлиннейшая последовательность такая: 3456789.

Вот сама программа:

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

my ($len,$d,$res)=(0);
$_='0123 1234345678910 ';
no warnings;

/((\d)
   (?{$d=$+})
   (?>
       (??{ ++$d < 10 ? "$d" : "(?!)" })*
   )
 )
 (?{                       	# Эти символы нельзя разделять
  if (length $1 > $len)
   { $len=length $1;
     $res=$1;
   }
 })                        	# Эту скобку нельзя отделять от предыдущей скобки "}"
 (?!)
 /x;
print $res if defined $res;

Вначале переменной $len присваивается 0. В комментариях указана особенность конструкций (?{ и }), в которых фигурную скобку нельзя отделять от соседнего символа даже и при свободном форматировании.

Теперь разберемся, как работает это регулярное выражение. Вначале открывается захватывающая скобка. Позже выяснится, что она захватывает очередную длиннейшую последовательность цифр, которые возрастают на единицу. Вторая пара захватывающих скобок захватывает одну цифру. Во второй строке стоит встроенный код Perl, который запоминает эту цифру в переменной $d. Далее идет динамическое регулярное выражение, к которому записан квантификатор *. Оно вставлено в атомарные скобки.

Код в этом динамическом регулярном выражении при каждой итерации, обусловленной квантификатором *, увеличивает на единицу переменную $d, и если ее значение меньше десяти, то он возвращает значение этой переменной, а если $d после инкремента принимает значение 10, которое уже не подходит для цифры, то этот код Perl возвращает уже знакомый нам подшаблон (?!), который заставляет квантификатор * сделать шаг назад и остановиться на своем предыдущем состоянии, при котором было совпадение с наибольшей цифрой. После этого совпадения для атомарных скобок управление передается за них и выполняется код Perl, который проверяет, больше ли длина переменной $1 длины переменной $len. Если больше, значит, мы нашли более длинную последовательность цифр, поэтому мы запоминаем ее в переменной $res и обновляем содержимое переменной $len, которая хранит размер найденной длиннейшей последовательности цифр. После этого кода опять стоит подшаблон (?!), который заставляет все регулярное выражение сделать повтор со следующего символа целевого текста.

Директива no warnings нужна, чтобы отменить предупреждение

matches null string many times in regex; …

которое появится, когда динамическое регулярное выражение вернет подшаблон (?!).

Perl предупреждает, что подшаблон, который не поглощает текст, имеет квантификатор. Но в нашем примере это не приводит к зацикливанию, т.к. этот подшаблон возвращает несовпадение, и происходит выход за пределы квантифицируемого подшаблона.

Здесь опять замечу, что это регулярное это выражение возвращает несовпадение и используется только ради побочного эффекта (установки переменной $res ).

Насколько эффективно работает это регулярное выражение? Вставьте встроенный код

Perl после атомарных скобок, который будет печатать содержимое переменной $1:

/((\d)
   (?{$d=$+})
   (?>
       (??{ ++$d < 10 ? "$d" : "(?!)" })*
   )
 ) (?{print "$1\n"})
 (?{                       # Эти символы нельзя разделять
  if (length $1 > $len)
   { $len=length $1;
     $res=$1;
   }
 })                        # Эту скобку нельзя отделять от предыдущей скобки "}"
 (?!)
 /x;
print $res if defined $res;

В результате на печати получим:

0123
123
23
3
1234
234
34
4
3456789
456789
56789
6789
789
89
9
1
0
3456789

Видим, что вначале регулярное выражение находит длиннейшую последовательность цифр, а потом перестартовывает с каждой следующей цифры этой последовательности благодаря второму подшаблону (?!). Хотелось бы, чтобы вместо этого начальная позиция при итерациях устанавливалась сразу за найденной последовательностью цифр, дабы не делать лишней работы. Но мне кажется, это невозможно, т.к. присвоение внутри регулярного выражения функции pos (pos($_)=…) не дает эффекта.

< Лекция 11 || Лекция 12: 12345 || Лекция 13 >
Гулзира Урбисинова
Гулзира Урбисинова
Сергей Крупко
Сергей Крупко

Добрый день.

Я сейчас прохожу курс  повышения квалификации  - "Профессиональное веб-программирование". Мне нужно получить диплом по этому курсу. Я так полагаю нужно его оплатить чтобы получить диплом о повышении квалификации. Как мне оплатить этот курс?