Динамические регулярные выражения
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
Это верный результат.