Еще об операции сопоставления
Мы уже рассматривали простейшие варианты использования операции сопоставления (регулярного выражения, заключенного между косыми). Теперь давайте изучим способы, которыми можно заставить эту операцию делать нечто иное.
Выбор другого объекта для сопоставления (операция :='•)
Обычно строка, которую нужно сопоставить с образцом, не находится в переменной $_, и помещать ее туда довольно утомительно. (Может быть, в переменной $__ уже хранится значение, которое вам не хочется терять.) Ничего страшного — здесь нам поможет операция =~. С ее помощью вы можете назначить для проведения операции сопоставления строку, хранящуюся в переменной, отличной от $_.
Эта переменная указывается справа от знака операции. Выглядит это так:
$а = "hello world";
$а =~ /^he/; # истина
$а =~ /(.)\1/; # тоже истина (соответствует двум 1)
if ($а =~ /(.)\1/) ( t истина, поэтому проводятся дальнейшие операции
1
Справа от знака операции =~ может стоять любое выражение, которое дает в результате некоторое скалярное строковое значение. Например, <stdin> при использовании в скалярном контексте дает скалярное строковое значение, поэтому, объединив эту операцию с операцией =~ и операцией сопоставления с регулярным выражением, мы получим компактную программу проверки входных данных:
print "any last request? ";
if (<STDIN> ==~ /л[y1}/) { # начинаются ли входные данные с буквы у? print "And just what might that request be? ";
# и чтобы это мог быть за запрос? <STDIN>; # получить строку со стандартного ввода print "Sorry, I'm unable to do that.\n";
# прошу прощения, но я не могу этого сделать )
В данном случае при помощи <stdin> берется очередная строка со стандартного ввода, которая затем сразу же используется как строка, сопоставляемая с образцом л [ yY ]. Отметим, что мы не сохраняли входные данные в переменной, поэтому если мы захотим сопоставить эти данные с другим образцом или же вывести их в сообщении об ошибке, то у нас ничего не выйдет. Тем не менее эта форма часто оказывается удобной.
Игнорирование регистра
В предыдущем примере мы указывали образец [yY] для обозначения строчной и прописной буквы у. Если речь идет об очень коротких строках, например, у или fred, то данный способ обозначения достаточно удобен, скажем, [fF] [rR] [eE] [dD]. А что делать, если сопоставляемая строка — это слово procedure в нижнем или верхнем регистре?
В некоторых версиях grep
флаг -i означает "игнорировать регистр". В Perl тоже есть такая опция. Чтобы ею воспользоваться, нужно добавить строчную i к закрывающей косой черте, т.е. написать / образец/i. Такая запись говорит о том, что буквы образца будут соответствовать буквам строки в любом регистре. Например, чтобы найти слово procedure в любом регистре, стоящее в начале строки, запишите /^procedure/i.
Теперь наш предыдущий пример будет выглядеть так:
print "any last request? ";
if (<STDIN> =~ /"y/i) { # начинаются ли входные данные с буквы у? # да! выполнить какие-то операции
…
}
Использование другого разделителя
Чтобы найти строку, которая содержит несколько косых (/), в соответствующем регулярном выражении нужно перед каждой из них поставить обратную косую черту (\). Например, чтобы найти строку, которая начинается с названия директории /usr/etc, нужно записать:
$path = <STDIN>; # прочитать путевое имя (вероятно, из find?) if ($path =~ /"VusrVetc/) {
# начинается с /usr/etc... }
Как видите, комбинация "обратная косая — косая" создает между элементами текста своеобразные "проходы". Если косых очень много, это занятие может стать весьма утомительным, поэтому в Perl предусмотрена возможность использования другого разделителя (delimiter). Поставьте перед любым специальным символом* (выбранным вами в качестве разделителя) букву т, укажите свой образец и дайте еще один такой же разделитель:
/''•VusrVetc/ # использование стандартного разделителя — косой черты m@^/usr/etc@ # использование в качестве разделителя символа @ m#^/usr/etc# # использование в качестве разделителя символа # # (это мой любимый символ)
Если хотите, можете опять использовать косые, например, m/fred/. Таким образом, m — общепринятое обозначение операции сопоставления с регулярным выражением, но если в качестве разделителя выбрана косая черта, то m не обязательна.
Использование интерполяции переменных
Перед тем как регулярное выражение рассматривается на предмет наличия специальных символов, в нем производится интерполяция переменных. Следовательно, регулярное выражение можно строить не только из литералов, но и из вычисляемых строк. Например:
$what = "bird";
$sentence = "Every good bird does fly.";
if ($sentence =~ /\b$what\b/) {
print "The sentence contains the word $what!\n";
>
Здесь мы использовали ссылку на переменную для построения операции сопоставления с регулярным выражением \bbird\b/.
* Если этот разделитель — левый элемент пары (круглая, фигурная, угловая или квадратная скобка), то закрывающим разделителем будет соответствующий правый элемент пары. В остальных случаях первый и второй разделители будут совпадать.
Вот несколько более сложный пример:
$sentence = "Every good bird does fly.";
print "What should I look for? ";
$what = <STDIN>;
chomp($what) ;
if ($sentence =~ /$what/) ( # нашли! print "I saw $what in $sentence.\n";
} else (
print "nope... didn't find it.\n";
)
Если вы введете слово bird, оно будет найдено, а если слово scream — не будет. Если ввести [bw] ird, результаты поиска тоже будут успешными. Это говорит о том, что квадратные скобки в данном случае воспринимаются как символы сопоставления с образцом.
Чтобы избежать этого, следует поставить перед этими символами обратную косую, которая превратит их в символы буквального сопоставления. Это кажется сложным, если в вашем распоряжении нет закавычивающей управляющей последовательности \Q:
$what = "[box]";
foreach (qw(in([box] out [box] white [sox] ) ) { if (/\Q$what\E/) {
print "$_ matched!\n";
1 }
Здесь конструкция \Q$what\E превращается в \[box\], в результате чего операция сопоставления ищет пару квадратных скобок, а не рассматривает всю конструкцию как класс символов.
Специальные переменные, защищенные от записи
После успешного сопоставления с образцом переменным $1, $2, $3 и т.д. присваиваются те же значения, что и \1, \2,\3 и т.д. Это можно использовать для поиска соответствия в последующем коде. Например:
$_ = "this is a test";
/(\w+)\W+(\w+)/; # сопоставление первых двух слов
# $1 теперь содержит this, а $2 — is
Доступ к тем же значениям ($1, $2, $3 и т.д.) можно также получить, использовав операцию сопоставления для соответствующих списков. Если результаты сопоставления окажутся положительными, будет получен список значений от $1 до $п (где n — количество занесенных в память элементов). В противном случае значения не определены. Запишем последний пример по-другому:
$_ = "this is a test";
($first, $second) = /(\w+)\W+(\w+)/; # сопоставление первых двух слов # $first теперь содержит this, a $second - is
К другим предопределенным защищенным от записи переменным относятся: $& (часть строки, совпавшая с регулярным выражением); $' (часть строки, стоящая перед совпавшей частью); $ ' (часть строки, стоящая после совпавшей части). Например:
$_ = "this is a sample string";
/sa.*le/; # соответствует слову sample внутри строки
# $' теперь содержит "this is a "
# $& теперь содержит "sample"
# $' теперь содержит "string"
Поскольку значения этим переменным присваиваются при каждом успешном сопоставлении, их нужно где-нибудь сохранить, если они вам впоследствии понадобятся*.