Извлечение соответствий
Когда операция сопоставления находит в строке соответствие указанному регулярному выражению, она присваивает результаты своей работы нескольким специальным переменным:
- в переменную $` помещается часть строки до найденного соответствия;
- в переменную $& помещается часть строки, соответствующая образцу;
- в переменную $' помещается часть строки после найденного соответствия;
- в переменную $+ помещается последнее найденное совпадение для последнего шаблона в скобках.
Если поиск окончился неудачей, то этим переменным новые значения не присваиваются. Посмотрим на примере, что сохранится в этих переменных после поиска такого соответствия:
$htm= "<A HREF='http://regexp.ru/'>Регулярные выражения</A>";
$htm =~ m|HREF=["'](\S+?)["']>|; # поиск URL сайта
При успешном совпадении с шаблоном в специальные переменные будут помещены такие значения:
$` = '<A '
$& = 'HREF='http://regexp.ru/'>'
$' = 'Регулярные выражения</A>'
$+ = 'http://regexp.ru/'
Значениями этих переменных можно пользоваться при успешном сопоставлении с образцом, например:
print $& if $text =~ m/$pattern/; # выведет соответствие
В регулярном выражении можно указать, что при успешном сопоставлении строки с шаблоном найденные соответствия нужно сохранить для дальнейшей обработки. С этой целью запоминаемые части шаблона нужно заключить в круглые скобки. Это также называется захватом значений. Найденные совпадения для всех заключенных в скобки частей шаблона будут доступны через специальные переменные с именами $1, $2 и так далее. Составим регулярное выражение для поиска и сохранения в служебных переменных информации о сайте в том же тексте:
$pattern = q|HREF=["'](\S+?)["']>([^<]+?)</A>|; # шаблон
$htm =~ m/$pattern/; # поиск соответствия в $htm
# в $1 = 'http://regexp.ru/'
# в $2 = 'Регулярные выражения'
Сохраненные совпадения доступны и во время обработки регулярного выражения, но через переменные с именами \1, \2 и так далее. Эти переменные называются обратными ссылками (backreference) на найденные соответствия. Так, например, можно найти два одинаковых слова, стоящих в тексте друг за другом через пробелы (возможно, по ошибке):
my $string = "Уже скоро скоро наступит весна!";
my $pattern = '(\S+)\s+\1';
# (\S+) сохранит значение 'скоро' в \1
$string =~ m/$pattern/; # соответствие: 'скоро скоро'
Операция сопоставления, употребленная в списочном контексте, возвращает список найденных соответствий, для которых было предусмотрено сохранение значений. Поэтому удобно сохранять найденные значения в массиве или в списке скалярных переменных. Например, извлечем из текстовой строки последовательность цифр, похожую на время:
my $text = 'Начало в 12:25:00.'; # строка с данными
my $pattern = '(\d\d):(\d\d):(\d\d)'; # образец для поиска
my @time = $text =~ m/$pattern/; # сохраним в массиве
my ($hh, $mm, $ss) = $text =~ m/$pattern/; # и в списке
Можно находить любое количество соответствий образцу в одной операции сопоставления. Это делается с помощью модификатора глобального поиска.
Модификаторы
До сих пор операция сопоставления прекращала работу и возвращала результат, когда находилось первое соответствие строки указанному шаблону. Если для операции сопоставления указать модификатор /g (global), то она будет искать в строке все соответствия образцу, организуя неявный цикл обработки регулярного выражения. Например, так можно найти все числа в строке с помощью одного шаблона:
my @numbers = 'Не 12.5, а 25!' =~ /(\d+)/g; # глобальный поиск
# в @numbers будет (12, 5, 25)
Ранее в этой лекции уже упоминался модификатор /i, устанавливающий поиск с игнорированием разницы между заглавными и строчными буквами. Перечислим модификаторы для операции сопоставления:
- /g - искать в тексте все соответствия образцу (Global);
- /i - искать соответствие образцу без учета регистра букв (case-Insensitive);
- /s - рассматривать текст как одну строку (Single-line);
- /m - рассматривать текст как многострочный (Multi-line) с учетом \n ;
- /o - один раз откомпилировать регулярное выражение (Once);
- /x - использовать расширенный синтаксис регулярных выражений (eXtended).
Из всех модификаторов, пожалуй, самый интересный - последний, который позволяет записывать регулярные выражения в структурированном и понятном для человека виде и даже сопровождать комментариями! Так, например, можно более понятно и красиво переписать регулярное выражение, приведенное в начале лекции:
m/ # начало регулярного выражения
<A # начало тега: <A
[^>]+? # далее могут быть любые символы, кроме >
HREF # определение гиперссылки
\s*=\s* # знак =, возможно окруженный пробелами
["']? # может быть открывающая кавычка или апостроф
( # начало захвата значения
[^'" >]+? # адрес ссылки: все, кроме ',",пробела и >
) # конец захвата значения
['"]? # может быть закрывающая кавычка или апостроф
\s* # за которым могут быть пробелы
> # конец тега
/igx; # конец регулярного выражения
# соответствует, например: <a id='ru' href="index.html">
Записанное в таком виде, регулярное выражение становится доступным для понимания, анализа и модификации. А посколькурегулярные выражения компилируются, то пробельные символы и комментарии не влияют на быстродействие программы.
Замена строк
Кроме поиска, регулярные выражения часто применяются для замены найденных совпадений на новые значения. Для этого существует операция замены (substitution), которая пытается найти в строковой переменной соответствие образцу, а если находит, то заменяет найденную подстроку на указанное значение. Операция замены выглядит так:
$variable =~ s/образец/замена/;
# в переменной $variable отыскивается строка 'образец',
# и если найдена, то она заменяется на 'замена'
Все, что говорилось до этого про операцию сопоставления, применимо для левой части операции замены, в которой указывается образец поиска. Левая и правая части операции замены интерполируются, поэтому там могут использоваться escape-последовательности и переменные.
$pattern = 'шило'; # образец
$replacement = 'мыло'; # замена
$text =~ s/$pattern/$replacement/; # поменять 'шило' на 'мыло'
В правой части операции замены могут использоваться обратные ссылки на найденные значения. Так, например, можно поменять местами два крайних слова в тройке слов, разделенных пробельными символами:
$text = 'мать любит дочь';
$text =~ s/(\S+)\s+(\S+)\s+(\S+)/\3 \2 \1/;
# в $text будет 'дочь любит мать'
Для операции замены s/// можно применять все модификаторы, упомянутые для операции сопоставления m//. Например, модификатор /g указывает, что должны быть заменены все найденные в тексте соответствия. Например:
$our_computers =~ s/Windows/Linux/g;
У операции замены есть дополнительный модификатор /e (expression evaluation), при включении которого заменяющая часть вычисляется как выражение. При этом в заменяющей части можно использовать ссылки на захваченные при помощи круглых скобок соответствия. Это можно применять для более "интеллектуальной" замены найденных соответствий. Так, например, можно перевести температуру из шкалы Цельсия в шкалу Фаренгейта:
$text = 'Бумага воспламеняется при 233C.';
$text =~ s/(\d+\.?\d*)C\b/int($1*1.8+32).'F'/e;
# в $text будет: 'Бумага воспламеняется при 451F.'
Применение регулярных выражений
Регулярные выражения применяются во многих конструкциях. В функции split() первым параметром может использоватьсярегулярное выражение, которое будет служить для поиска разделителей при разделении строки на части. Так, например, можно разбить строку на подстроки по любому из пробельных символов:
@substrings = split /\s+/, $text; # разбить на части
Регулярные выражения часто применяются в функциях, работающих с массивами для фильтрации нужных элементов. Например,функция grep() возвратит список элементов массива, соответствующих указанному образцу:
@result = grep /$pattern/, @source; # отобрать элементы
С помощью функции map можно применить операцию замены в соответствии с регулярным выражением ко всем элементам массива, например:
@hrefs = ('http://regex.info', 'http://regexp.ru');
map s{http://}{}, @hrefs; # убрать 'http://' из ссылок
Регулярные выражения дают программисту новый взгляд на текстовые данные: вместо отдельных символов и простых подстрок он начинает мыслить обобщенными шаблонами, что помогает ему находить более простые и эффективные решения. В таблице 8.1 для справки приведены основные обозначения, применяемые для записи регулярных выражений в Perl. Дополнительные сведения о регулярных выражениях можно почерпнуть из стандартной документации по Perl и перевода уникальной книги Джеффри Фридла "Регулярные выражения".
Таблица 8.1. Основные обозначения для записи регулярных выражений
Обозначение |
Описание |
Примеры |
// |
ограничители регулярного выражения по умолчанию |
/$pattern/ |
\ |
отмена специального значения следующего символа |
m{C:\\windows} |
() |
группировка шаблонов или сохранение значения |
/(\w\w\w)+/ |
| |
выбор из нескольких альтернатив |
/кошелек|жизнь/ |
[] |
класс символов: любой символ из перечисленных |
/[0-9a-fA-F]/ |
[^] |
инвертированный класс символов: любой символ, кроме перечисленных |
/[^0-9]/ |
Метасимволы |
. |
любой символ, кроме \n (соответствует любому символу, включая \n с модификатором /s) |
/(.+)/ |
\d |
десятичная цифра |
m{Время=\d+ сек} |
\D |
не десятичная цифра |
/(\D*)\d+/ |
\w |
алфавитно-цифровой знак |
/\s+\w+\s+/ |
\W |
не алфавитно-цифровой знак |
/\W\W\W/ |
\s |
пробельный символ |
s/\s+/ / |
\S |
любой символ, кроме пробельного |
/\S+/ |
Утверждения |
^ |
начало строки (соответствует началу каждой строки с модификатором /m) |
/^\w+/ |
$ |
конец строки (соответствует концу каждой строки с модификатором /m) |
/\d+$/ |
\b |
граница слова (между \w и \W или \W и \w) |
/stop\b/ |
\B |
любая позиция, кроме границы слова |
/stop\B/ |
\A |
только начало строки, даже с модификатором /m |
/\A[#]/ |
\z |
только конец строки, даже с модификатором /m |
/\w+\z/ |
\Z |
только конец строки или перед \n в конце строки, даже с модификатором /m |
/\w+\Z/ |
\G |
позиция в строке, равная значению функции pos() |
|
Escape-последовательности |
\t \n \r \f \a \b |
управляющие символы: \b в классе символов выступает как символ Backspace (0x08), вне его - как граница слова |
/[\a\b\f\r\n\t]/ |
\0 \x \c \N |
коды символов |
/\033\x1F\cZ/ /\x{263a}/ |
\l \L \u \U \Q \E |
преобразующие последовательности |
/\Q$pattern\E/ |
Квантификаторы |
* *? |
любое число повторений, включая 0 (максимальный и минимальный квантификаторы) |
/\s*/ /\S*?/ |
+ +? |
одно и более повторений (максимальный и минимальный квантификаторы) |
/\d+/ /\D+?/ |
? ?? |
ноль или одно повторение (максимальный и минимальный квантификаторы) |
/.?/ /[.a-z]??/ |
{n} {n}? |
ровно n повторений (максимальный и минимальный квантификаторы) |
/\w{8}/ /\w{5}?/ |
{n,} {n,}? |
n и более повторений (максимальный и минимальный квантификаторы) |
/\d{2,}/ /\d{5,}?/ |
{n,m} {n,m}? |
от n до m повторений включительно (максимальный и минимальный квантификаторы) |
/[A-Z]{1,12}/ /[a-z]{0,3}?/ |
|