Специальные переменные, используемые в регулярных выражениях
11.3. Имитация именованного сохранения
Регулярные выражения в 5-ой версии Perl не поддерживают именованного сохранения найденных фрагментов текста, а поддерживают только переменные с номером. Это создает неудобства, а при использовании объектов регулярных выражений, которые сохраняют найденные фрагменты текста, тяжело модифицировать эти объекты, если надо ввести новую сохраняющую переменную. В 6-й версии Perl должно появиться именованное сохранение, когда вместо номера переменной можно задать ей произвольное имя и обращаться к такой переменной по имени. А пока для этого остается использовать встроенный код и специальную переменную $^N. Например, у нас есть объект регулярного выражения, который сохраняет URL:
my $re=qr/<a\s+href="([^"]+)"/i;
Мы можем его использовать в коде
$_='<a href="http://www.intuit.ru">'; print $1 if /$re/;
И напечатается
http://www.intuit.ru
URL получается в переменной $1. Если объект регулярного выражения $re входит кирпичиком в более крупное регулярное выражение, то нумерованные переменные использовать рискованно, т.к. при вставке новых объектов или редактировании существующих нумерация может сбиться и URL может оказаться уже не в переменной $1, а в другой нумерованной переменной. Мы можем объявить переменную $url и всегда сохранять в ней найденный URL, воспользовавшись тем, что переменная $^N является копией нумерованной переменной, которая соответствует последней паре захватывающих скобок:
my $url; my $re=qr/<a\s+href="([^"]+)(?{$url=$^N})"/i; $_='<a href="http://www.intuit.ru">'; print $url if /$re/;
Опять напечатается
http://www.intuit.ru
Подобный кирпичик $re может войти в здание более сложного регулярного выражения, не влияя на другие переменные, сохраняющие фрагменты текста. Но встроенный код, конечно, требует дополнительного времени для выполнения.
11.4. Избавление от "вредных" специальных переменных и предварительного копирования текста
Перед применением регулярного выражения Perl, как правило, создает копию целевого текста. Это нужно, например, если после использования регулярного выражения идет обращение к нумерованным переменным. Ведь программист может изменить целевой текст, но содержимое нумерованных переменных не должно меняться, чтобы не вызвать неразбериху.
Perl не создает каждый раз при использовании регулярного выражения специальные переменные $1, $+, $` и т.д., он просто создает копию целевого текста и эти переменные ссылаются на фрагменты текста в этой копии. Это экономит время, ведь не всегда эти переменные потом будут затребованы программистом.
Целевой текст может оказаться большим, и система вынуждена будет тратить время и память на его дублирование, хотя этот дубль может и не понадобиться. Если мы не используем захватывающих скобок, будет ли Perl создавать копию целевого текста?
Будет, т.к. существуют еще переменные $`, $& и $'. Perl не может решить, к какому регулярному выражению применяются эти переменные, и будет создавать копию целевого текста каждый раз, несмотря на отсутствие захватывающих скобок. По этой причине переменные $`, $& и $' называются "вредными". Кроме того, их применение замедляет работу программы.
Транслятор просматривает всю программу и все используемые ею модули на предмет наличия этих "вредных" переменных. И если он их не находит, то выпоняет оптимизацию программы: не создает копию целевого текста для регулярных выражений, которые не используют захватывающих скобок. Базовые модули, которые входят в поставку Perl, за исключением модуля English, не используют этих переменных.
"Вредные" переменные $`, $& и $' можно имитировать с помощью массивов @- и @+. (Предполагаем, что целевой текст находится в переменной $_ ):
- $` соответствует substr($_, 0, $-[0])
- $& соответствует substr($_, $-[0], $+[0] - $-[0])
- $' соответствует substr($_, $+[0])