Регулярные выражения Perl и их применение

       

Экранирование метасимволов регулярных выражений


Если вы вставляете в регулярное выражение литерал из ввода пользователя или какую-либо переменную и хотите, чтобы переменная интерпретировалась как текст для поиска, а не как часть регулярного выражения, то для этого имеется эскейп-последовательность \Q, которая включает режим экранирования метасимволов регулярных выражений, таких, как [, *, +, {, ^, $, …: /\Q$userinput\E/. Экранирование осуществляется до эскейп-последовательности \E, а при ее отсутствии - до конца регулярного выражения.

Вставка ввода пользователя в регулярное выражение сопряжена с опасностью выполнения постороннего кода Perl, который может содержать вызов системных программ. Это является большой дырой в защите сервера. Поэтому переменную, содержащую ввод пользователя, надо заключать в специальную конструкцию \Q…\E. Вряд ли вы захотите искать или заменять что-то по регулярному выражению, которое вводит пользователь.

Результат аналогичен применению функции Perl quotemeta(). Но символы \ в неизвестных комбинациях (например, \F) не экранируются. Переменные типа $scalar и @array внутри метапоследовательности \Q…\E интерполируются, также интерполируются специальные переменные, которые должны интерполироваться внутри регулярных выражений.

Например:

$_='[a]bc$ '; print "'$&'" if /^\Q[a]bc$ \E$/;

Напечатается '[a]bc$ '. После символа $ внутри строки $_ и регулярного выражения стоит пробел. Если бы между символами $ и \ не было пробела, то внутри регулярного выражения возникла бы специальная переменная $\, которая стандартно содержит неопределенное значение. Она бы заменилась на пустой фрагмент, символ \ был бы отнят от буквы E и вместо завершителя метапоследовательности \Q…\E и метасимвола $ - конца текста - мы бы получили букву E, за которой стоит простой символ $:

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

$_='[a]bcE$'; print $& if /^\Q[a]bc$\E$/;

На печати оказывается следующее:

Use of uninitialized value in concatenation (.) or string at a.pl line 6. [a]bcE$

Мы получили предупреждение об использовании неинициализированной переменной ( а именно, $\) внутри регулярного выражения. Но совпадение все же было найдено.

Чтобы яснее проиллюстрировать этот пример, присвоим переменной $\ какое-либо значение, а также вставим его в исходную строку после буквы c:

$\='?'; $_='[a]bc?E$'; print $& if /^\Q[a]bc$\E$/;


Совпадение найдется без предупреждений, и на печати окажется

[a]bc?E$?

Теперь все ясно, только откуда взялся последний знак вопроса? Дело в том, что специальная переменная $\ содержит текст, который оператор print использует как завершитель выходных записей и добавляет после своего вывода, - вот он этот текст и добавил.

Получается, что символы $ и @ внутри последовательности \Q…\E будут интерполироваться, если за ними следуют символы, которые встречаются в именах переменных или специальных переменных. А если, как у нас, после $ будет пробел, то тогда символы $ и @ будут обозначать сами себя. Ничего не даст попытка экранировать их обратным слэшем: \Q\$a\E - этот обратный слэш будет сам экранирован внутри последовательности \Q…\E, и мы получим фрагмент текста $a.

Но внутри переменных интерполяция переменных не происходит, также в них не распознаются метасимволы \Q и \E литералов регулярных выражений:

$_='[a]\\Qbc$\\'; my $a='[a]\\Qbc$\\'; print $& if /^\Q$a\E$/;

На печать выходит

[a]\Qbc$\

Также внутри блоков \Q…\E работают метасимволы литералов регулярных выражений \U, \u, \L и \l.

  • \L означает, что все буквы до эскейп-последовательности \E или до конца литерала регулярного выражения будут прописными.
  • \l означает, что следующий символ, если он буква, будет прописным.
  • \U означает, что все буквы до эскейп-последовательности \E или до конца литерала регулярного выражения будут заглавными.
  • \u означает, что следующий символ, если он буква, будет заглавным.


Например:

$_='abc'; my $a='ABC'; print $& if /^\L$a/;

Будет напечатано abc.

Или

$_='abc'; print $& if /^\LABC/;

Здесь также напечатается abc.

Обратите внимание на такой нюанс:

$_='a'; print "Found $&" if /^\Q\LA\E$/;

Не найдено!



Печатает

Found a$

Замечу еще, что пустая метапоследователность \Q\E вызывает ошибку синтаксиса, так же как и \U\E и \L\E. Комбинации \U\L и \L\U также почему-то вызывают ошибки синтаксиса.

Внутри классов все эти метапоследовательности \u, \l, \U…\E, \L…\E, \Q…\E также работают. Вот примеры:

$_='A'; print $& if /[\ua]/;

Напечатает A.

$_='AB]'; print $& if /[\Ua]b\E]/;

Напечатает AB].

Здесь обратите внимание на "сквозное" действие метапоследовательности \U…\E, которая продолжает действовать за пределами класса. Ведь рассматриваемые метапоследовательности применяются сразу после интерполяции переменных, поэтому механизм поиска соответствия получит регулярное выражение /[A]B]/.

Вот пример полезной идиомы: в переменной $name задано имя человека буквами произвольного регистра. Мы с помощью последовательности \u\L в любом случае делаем первую букву заглавной, а остальные - строчными:

$_='Andrey'; my $name='aNDreY'; print $& if /\u\L$name\E/;

Напечатает Andrey.

Внутри классов это тоже работает:

$_='Andrey'; my $name='aNDreY'; print $& if /[\u\L$name\E]{6}/;

Будет напечатано Andrey. Регулярное выражение будет иметь вид /[Andrey]{6}/.

Еще раз заметиме, что внутри переменных эти метапоследовательности не распознаются.

Например:

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

$_='A'; my $name='\\ua'; print $& if /$name/;

Возникает сообщение:

Unrecognized escape \u passed through in regex; marked by <-- HERE in m/\u <-- HERE a/ at a.pl line 6.

В регулярное выражение мы передали строку \ua. Но в интерполируемых переменных рассматриваемые метапоследовательности не работают.

Сделаю еще одно замечание насчет использования \u, \l, \U…\E и \L…\E. В документации алгоритм их работы не разъяснен, не написано, какую ассоциативность имеют эти операторы - левую или правую. Ведь их действия могут конфликтовать друг с другом. К примеру, какой напечатается строка "\Ua\lAa"? Эскейп-последовательность \l говорит, что следующая буква A будет прописной, а \U говорит, что все после нее до конца строки будет заглавным. В итоге напечатается AAA. Зесь мы видим, что эти операторы имеют правую ассоциативность, т.е. выполняются справа налево. То же справедливо, когда рядом стоят символы \l\u и \u\l. Однако, если вместе стоят символы \U\l, \l\U, \L\u и \u\L, то префиксы \l и \u имеют приоритет перед метасимволами \L и \U. Сравните результаты:

print "\L\uaA\n"; print "\LaA\uaA\n";


Содержание раздела