Построение эффективных регулярных выражений. Оператор qr/…/ и объекты регулярных выражений.
10.1.1. Оператор qr/…/ и интерполяция переменных
Если интерполируемые переменные, которые используются в операторе qr/…/, после трансляции им регулярного выражения изменят свое значение, то это не отразится на объекте регулярного выражения, даже если оператор qr/…/ не имеет модификатора o.
Рассмотрим такие примеры:
my $a=$_='ab'; my $re=qr"$a"; print "Found $&\n" if /$re/; $_=$a='cd'; print "Found $&\n" if /$re/;
Будет напечатано
Found ab
Во второй раз поиск закончился неудачей. Но если опять присвоить переменной $re значение qr"$a", то будет найдено и cd:
my $a=$_='ab'; my $re=qr"$a"; print "Found $&\n" if /$re/; $_=$a='cd'; $re=qr"$a"; print "Found $&\n" if /$re/;
Напечатается
Found ab Found cd
Если распечатать содержимое переменной, содержащей объект регулярного выражения, то на печать выйдет исходное регулярное выражение, отформатированное интерпретатором:
my $re=qr/ [a-z]+ # все буквы /x; print $re;
Напечатается
(?x-ism: [a-z]+ # все буквы )
Оператор qr/…/ полезен не только тем, что заранее компилирует регулярное выражение, которое потом применяется в готовом виде, но и тем, что позволяет строить большие и сложные регулярные выражения по кирпичикам. Например:
$_='ABCabc123'; my $re1=qr/[A-Z]+/; my $re2=qr/(\d+)/; print "$&\n$1\n$2" if /$re1([a-z]+)$re2/;
Напечатается
ABCabc123 abc 123
Если внутри какого-то объекта используются захватывающие скобки, то возникает сложность и неудобство при модификации такого регулярного выражения: ведь если впереди будет вставлена переменная или непосредственный текст регулярного выражения, который тоже содержит захватывающие скобки, то нарушится нумерация нумерованных переменных и обратные ссылки могут уже не соответствовать захватывающим скобкам, которые раньше были их "родными". Например:
$_='abc111abc'; my $re1=qr/[a-z]+/; my $re2=qr/(\d+)\1/; print "$&\n$1" if /$re1$re2/;
Напечатается
abc11 1
А в случае
$_='abc111abc'; my $re1=qr/([a-z]+)/; my $re2=qr/(\d+)\1/; print "$&\n$1" if /$re1$re2/;
напечатается
abc111abc abc
В первом примере обратная ссылка \1 имела значение 1, а во втором - abc, т.е. стала относиться к нумерованной переменной из другого объекта регулярного выражения.
В принципе, в этих двух случаях может помочь переменная $^N, которая дублирует значение нумерованной переменной, соответствующей последней только что закрытой паре захватывающих скобок. Если вам надо сохранить значение нумерованной переменной, то сразу после того, как закроется ее скобка, вставьте код Perl
(?{ $s=$^N })
и последнее значение нумерованной переменной сохранится во внешней переменной $s.
Чтобы не путались номера обратных ссылок, можно (забегая вперед) воспользоваться динамическими регулярными выражениями, которые позволяют подставлять вместо себя текст регулярного выражения, который возвращает этот код. Динамическое регулярное выражение вводится конструкцией
(??{ код Perl })
Возвращаемое значение этого кода Perl интерпретируется как текст и как часть общего регулярного выражения.
В последнем примере можно записать так:
$_='abc111abc'; my $re1=qr/([a-z]+)/; my $re2=qr/(\d+)(??{$^N})/; print "$&\n$1" if /$re1$re2/;
На печати получим
abc11 abc
Отсюда видим, что динамическое регулярное выражение (??{$^N}) соответствует обратной ссылке \2. Как и во встроенном коде Perl, в динамических регулярных выражениях переменные не интерполируются при компиляции шаблона, а каждый раз вычисляются заново. Другое дело, что динамические регулярные выражения и встроенный код Perl требуют увеличения времени на работу регулярного выражения.