Опубликован: 18.06.2007 | Доступ: свободный | Студентов: 1420 / 43 | Оценка: 4.14 / 3.29 | Длительность: 12:44:00
ISBN: 978-5-94774-604-4
Лекция 1:

Создание регулярных выражений

Лекция 1: 12345 || Лекция 2 >

1.1.5 Захватывающие и незахватывающие скобки

Поиск соответствия давал бы мало пользы, если бы нельзя было извлекать из текста интересующие нас фрагменты. Для извлечения фрагмента текста часть шаблона (или весь шаблон ), который ему соответствует, надо заключить в круглые скобки. Всего в регулярном выражении может быть 99 захватывающих пар скобок. Такие скобки также называют сохраняющими. Если вы не хотите сохранять часть текста, а только группируете подшаблоны, то для этого существуют обычные скобки, которые не сохраняют текст; таких скобок в регулярном выражении может быть 200 пар. Чтобы сделать пару скобок обычной ( несохраняющей ), надо сразу после открывающей скобки поставить вопросительный знак и двоеточие. Сохраняющие и несохраняющие скобки могут иметь какой угодно уровень вложенности. Сохраняющие скобки нумеруются в порядке появления открывающей скобки от 1 до 99, чтобы за пределами оператора поиска иметь сохраненными нужные фрагменты текста. Текст, сопоставленный подшаблону в первой паре захватывающих скобок, окажется в специальной переменной $1, сопоставленный второй паре захватывающих скобок - в переменной $2 и т.д. до $99.

Разумеется, не обязательно иметь 99 пар скобок, - незадействованные специальные переменные будут иметь неопределенное значение. Обратите внимание, что нумерация начинается не с нуля и что переменная $0 не имеет отношения к регулярным выражениям, а хранит имя файла выполняемого сценария.

Рассмотрим такой пример: пусть в переменной $text хранится текст для тега a

<a href="http://www.intuit.ru/">Internet-обучение</a>

Нам надо написать регулярное выражение, которое соответствует тегу a и извлекает из него ссылку. Можно написать так:

$text =~ m#<a href="([^"]+)">[^<]+</a>#;
print $1;

Заметьте, что в качестве символов-ограничителей были выбраны решетки, чтобы избежать частокола замаскированных символов /, которые встречаются в регулярном выражении. В результате на печать выведется ссылка

http://www.intuit.ru/

Сначала в регулярном выражении идет литеральный текст

<a href="

который один к одному соответствует своему двойнику в целевой строке $text. Затем в целевой строке стоит ссылка, которую и надо получить. Поэтому мы открываем захватывающую скобку. Подшаблон

[^"]+

означает "любое число символов от 1 и более, которые не содержат кавычек". Поэтому скобки сохранят нам весь текст до следующих кавычек, а это и есть ссылка. Дальше в шаблоне опять идет литерал >, а подшаблон

[^<]+

соответствует всему до открывающей угловой скобки, затем вновь идет литеральный текст, который соответствует такому же тексту в переменной $text.

Записанное регулярное выражение несовершенно. Если после <a будет не один пробел, а больше, или будет стоять перевод строки, то оператор поиска не найдет соответствия и переменная $1 будет иметь неопределенное значение (или значение, которое осталось от предыдущего оператора поиска или замены). Имейте в виду, что нумерованные переменные $1, …, $99 изменяются только при успешном поиске! Кроме того, надежнее вести поиск без учета регистра символов. С учетом этих требований, получаем такую версию нашей программы:

$text='<a href="http://www.intuit.ru/">Internet-обучение</a>';
$text =~ m#<a\s+href="([^"]+)">[^<]+</a>#i;
print $1;

После <a вместо пробела теперь стоит подшаблон \s+, он берет на себя все пробельные символы, один из которых обязательно должен встретиться по правилам HTML. Кроме того, появился модификатор i. Но правила не запрещают иметь пробельные символы перед и после знака равенства, это тоже надо учесть и вставить вокруг него конструкции \s*. Надо также что-то придумать насчет кавычек, ведь на их месте могут быть апострофы или вообще ничего. Модернизированный вариант выглядит так:

$text='<a href="http://www.intuit.ru/">Internet-обучение</a>';
if ($text =~ m#<a\s+href\s*=\s*["']?([^"'> ]+)["']?>[^<]+</a>#i) { print $1 }

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

Теперь подшаблон ["']? возьмет на себя кавычку или апостоф только в случае, если этот символ присутствует. Внутри захватывающих скобок надо захватывать все символы кроме кавычки, апострофа, закрывающей угловой скобки и пробела, затем опять может идти кавычки или апостроф (а может, и нет), а далее все по-старому.

Это уже лучше, но в теге a могут быть параметры, например, target=_blank, как быть тогда? Тогда наш оператор поиска закончится неудачей, ведь в регулярном выражении после ссылки предусмотрены апостроф или кавычка и затем сразу идет закрывающая скобка. Параметру target=_blank ничего не будет соответствовать.

Это легко обойти, вставив перед закрывающей скобкой подшаблон

[^>]*

который поглотит все, что будет стоять до этой скобки.

Это хорошо, но ведь параметры тега a ключевые, а не позиционные, и параметр href не обязан стоять первым. Тег может быть оформлен так:

<a target="_blank" href="http://www.intuit.ru/">Internet-обучение</a>

В этом случае наш оператор поиска не найдет соответствия. Надо пропускать символы, пока не встретится href. Это можно сделать с помощью конструкции

.*?

и не забыть поставить модификатор s, потому что тег может располагаться на нескольких строках (после target="_blank" может быть перевод строки), а метасимвол "точка" без этого модификатора не соответствует символу перевода строки (new line).

Эта конструкция будет пропускать все символы, пока не встретится подстрока href. Но вообще говоря, так мы можем выскочить за границу тега >, если в теге не встретится href. Чтобы увеличить надежность нашего регулярного выражения, можно вместо этой конструкции поставить другую:

[^>]*?

Теперь модификатор s можно не ставить.

Еще я советую использовать директиву

use strict;

чтобы транслятор проверял, все ли переменные определены, и параметр w для выдачи предупреждающих сообщений транслятора.

Если вы запускаете скрипт на Web-сервере из браузера, то вставьте также директиву

use CGI::Carp qw(fatalsToBrowser);

чтобы Perl выводил ошибки в браузер, иначе вы будете долго гадать, почему скрипт не работает.

Вот законченная программа, которая "железобетонно" выводит ссылку из тега a:

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

my $text='<a target="_blank" href="http://www.intuit.ru/">Internet-обучение</a>';
if ($text =~ m#<a\s+[^>]*?href\s*=\s*["']?([^"'> ]+)["']?[^>]*>[^<]+</a>#i) { print $1 }

Если вы будете запускать Perl -программу на сервере Unix, то запоминайте текст в файл без символов возврата каретки \r. В редакторе Far это можно сделать по клавишам <Shift>+<F2>. Если вывод скрипта будет направлен веб-серверу и от него браузеру, то перед первым выводом (оператором print ) должна идти команда

print "Content-Type: text/html\n\n";

чтобы веб-сервер знал формат содержимого. Попробуйте менять способ оформления тега, оператор поиска должен будет неизменно находить результат.

Лекция 1: 12345 || Лекция 2 >
Гулзира Урбисинова
Гулзира Урбисинова
Сергей Крупко
Сергей Крупко

Добрый день.

Я сейчас прохожу курс  повышения квалификации  - "Профессиональное веб-программирование". Мне нужно получить диплом по этому курсу. Я так полагаю нужно его оплатить чтобы получить диплом о повышении квалификации. Как мне оплатить этот курс?