Динамические регулярные выражения
12.2. Поиск вложенных конструкций
Изначально динамические регулярные выражения создавались для того, чтобы искать вложенные конструкции произвольного уровня вложенности. Мы уже разбирали такой пример, который делал это с помощью встроенного кода Perl. Сейчас рассмотрим эти примеры с использованием динамических регулярных выражений.
Рассмотрим задачу удаления всех комментариев, которые заключены в фигурные скобки, вместе с этими скобками. При этом предположим, что эти комментарии могут иметь произвольную вложенность. Т.е. надо уничтожить все правильно сбалансированные серии фигурных скобок вместе с их содержимым. От строки
$_=" {a{b{c{{d{}m}e}f}}gf{ }f";после этого должно остаться ' {agff'.
Будем рассуждать от легкого случая к более сложным. Если бы речь шла только о первом уровне вложенности: …{…}…, то мы могли бы создать такой объект регулярного выражения:
my $level0=qr/(?>[^{}]*)/;который соответствует всему кроме фгурных скобок. А оператор замены был бы таким:
s/\{$level0}//g;Напоминаю, что символ } не обязательно маскировать, т.к. он в отличие от символа { не является метасимволом регулярного выражения.
Эта программа корректно бы удаляла комментарии первого уровня вложенности. От строки a{b}c осталось бы ac. Но если бы мы задали этой программе строку
a{b{c}d}eто в результате получили бы остаток
a{bd}eУдаляются только скобки с фрагментами, которые не содержат этих скобок. Мы могли бы повторять подстановку, пока оператор s/…/…/ возвращает ненулевой результат, но нам нужен общий метод для любого уровня вложенности. С этой целью расширим наш объект регулярного выражения. Он должен совпадать не только с текстом без скобок, но и с текстом без скобок, который ограничен этими фигурными скобками. Это мы сделаем с помощью конструкции альтернативного шаблона:
my $level1=qr/(?>[^{}]|\{$level0})*/;Здесь мы воспользовались тем, что у нас уже есть регулярное выражение, которое соответствует фрагменту текста без фигурных скобок, это объект $level0. Чтобы фрагменты текста без скобок поглощались быстрее, можно поставить квантификатор +:
my $level1=qr/(?>[^{}]+|\{$level0})*/;А чтобы не создавалось ненужных сохраненных состояний, можно это еще взять в атомарные скобки:
my $level1=qr/(?>(?>[^{}]+)|\{$level0})*/;$_='a{b{c}d}e';
my $level0=qr/(?>[^{}]*)/;
my $level1=qr/(?>(?>[^{}]+)|\{$level0})*/;
s/\{$level1}//g;
print $_;уже справляется со скобками второго уровня вложенности, и на печать выводится
ae
Но если ей дать текст со скобками третьего уровня вложенности, то она оставляет скобки первого уровня вложенности:
$_='a{b{c{d}}e}f';
my $level0=qr/(?>[^{}]*)/;
my $level1=qr/(?>(?>[^{}]+)|\{$level0})*/;
s/\{$level1}//g;
print $_;Выводится
a{be}fМы могли бы по аналогии создать объект $level3 и т.д., но динамические регулярные выражения позволяют сразу создать объект $levelN для произвольного уровня вложенности:
#!/usr/bin/perl -w
use strict;
$_=" {a{b{c{{d{}m}e}f}}gf{ }f";
my $levelN;
$levelN=qr
/(?>
(?>[^{}]+)| # все кроме фигурных скобок
\{(??{$levelN})} # или текст, соответств. всему шаблону, ограниченный скобками
)* # сколько угодно раз
/x;
s/\{$levelN}//g;
print $_;В результате получаем
{agffЭто верный результат.