В процессе программирования, перед программистом встают задачи, которые можно решать разными методами. Регулярные выражения - это миниязык, предназначенный для решения части этих задач. Причем, часть эта довольна увесистая. Недаром язык регулярных выражений в том виде, в котором он распространен больше всего, вышел из Perl и был настолько тесно с ним интегрирован, что некоторые считают его полноценной частью.
Однако, язык регулярных выражений настолько необычен, насколько полезен. Его изучение дается нелегко, но усилия оправдываются. Эта статья посвящена регулярным выражениям. В ней нет ни самоучителя, ни перечисления бестий регулярных выражений, лишь некоторые рассуждения и бонус - несколько RegExp-ов, выполняющих частовстречающиеся задачи.
Задачи, которые решают программисты, используя регулярные выражения, можно условно разделить на три класса:
Последний пункт можно так же, разбить на два подпункта: вычленение и сохранение данных в переменных и массивах, и удаление из текста ненужных частей, чтобы осталась только нужная часть.
Еще можно классифицировать регулярные выражения по количеству строк, с которыми они должны работать. Некоторые регулярки снабжаются специальными модификаторами, которые делают их "многострочными".
Особняком стоят регулярные выражения поиска и замены, в которых, в качестве замены, стоит функция, принимающая определенные параметры из шаблона поиска, производящая сложные вычисления и выдающая результат. Такие регулярки уже могут составлять целые мини-приложения.
Будем писать наши популярные регулярные выражения, исходя из первой классификации.
Давайте подумаем, когда нам нужно узнать, содержит ли переменная что-то? Довольно часто такая задача встает при проверке входящих данных. Как правило, это параметры или данные форм, реже - данные из файлов, заголовки ответов и коды страниц. Что ж, попробуем составить парочку частоупотребительных регулярных выражений.
Проверка корректности e-mail:
/^[a-z0-9.-]+@[a-z0-9.-]+\.\w{2,6}$/i
Модификатор "i" в конце регулярного выражения делает его нечувствительным к регистру символов, так что одинаково пройдут строки MAIL@MAIL.RU и mail@mail.ru.
Часто бывает нужно проверить, содержит ли переменная что-то, кроме цифр. Это можно просто сделать так:
/^\d+$/
Это утверждение будет справедливым, если в переменной есть хотя бы одна цифра. Если цифр может и не быть, вместо + нужно использовать *.
Возьмем задачку посложнее: проверим, есть ли в строке символы, закодированные url-encode.
/%[a-f0-9][a-f0-9]/i
Если в строке встретится последовательность символов вида "%F9", данная регулярка вернет истину.
О! Придумал! Давайте проверим, есть ли в тексте ссылка на нужный нам сайт? Приведу фрагмент кода:
$text = qq(<p>Некоторый <span>код</span>, имеющий <a href="http://site.ru">ссылки</a> и какие-то нужные</p><p>данные</p> Нужно проверить ссылки <a href=site2.ru>на соответствие</a>, нужному <a href =
' http://www.mysite.ru'>адресу</a>);
$needsite = 'mysite.ru';
if ($text =~ /href\s*=\s*(["']?)\s*(?:http:\/\/)?(?:www\.)?$needsite\s*\1/gi)
{print qq(<div style="background-color: powderblue;">Есть такой сайт</div>)}
else
{print "Такого сайта нет"}
В переменной $text есть текст, который нужно проверить на ссылку. Конечно, данный метод не гарантирует наличие именно ссылки (в тексте может быть текст "href=нужный сайт"), однако, с большой долей вероятности, можно утверждать, что если система "дала добро", ссылка все-таки есть.
Но хватит проверять. Давайте позаменяем.
Давайте, для разминки, заменим bb-теги "[b][/b]" на html-аналоги "<b></b>"?
s/\[b\](.*?)\[\/b\] /<b>$1<\/b>/igx
В данном случае, я добавил модификатор "x", который сделал возможным "раздвигание" регулярки, для улучшения читаемости.
Теперь давайте переведем дату из формата MySQL в наш, отечественный формат.
$date =~ s/(\d{4})-(\d{2})-(\d{2})/$3.$2.$1/;
Что-то я вошел во вкус:) Давайте обезопасим входной параметр? Готов поделиться фунцией.
sub soap
{
my $str = shift;
$str =~ s@\\@\\\\@g;
$str =~ s@('|")@\\$1@g;
return $str;
}
Эта функция принимает текст, экранирует в нем обратные слеши, а так же одинарную и двойную кавычки. Возвращает она уже "очищеный" текст. Конечно, это - всего лишь "первичная обработка".
Давайте теперь, используя пример про поиск ссылки на заданый сайт в тексте, создадим регулярное выражение, которое будет заставлять все ссылки в тексте ссылаться на заданный сайт (привет, грабберы!)
$text = qq(Некоторый <span>код хтмл</span>, имеющий <a href="http://site.ru">ссылки</a> и какие-то нужные <p>данные.</p> Нужно заменить все <a href=site2.ru>
ссылки</a>, подставив нужный адрес, место старого <a href='http://www.anothersite.com'>адреса</a>.);
$needsite = 'http://mysite.ru';
$text =~ s/href\s*=\s*(["']?)\s*(?:http:\/\/)?(?:www\.)?.+?\1/href="$needsite"/gsi;
Вообще, нужно сказать, регулярные выражения - довольно часто применяются в несовсем чистых целях. Я имею ввиду похищение информации с чужих сайтов. Ну что ж поделать - электронную почту тоже приспособили под спам. Видимо, это черта человека - приспосабливать всё хорошее под плохие дела (оглядываюсь в сторону ядерных реакторов и оружия).
Давайте теперь немного позапоминаем.
Часто нам бывает нужно не просто узнать есть ли данные где-то, а еще и получить их. Для этого можно пойти двумя путями: убрать все лишнее, или наоборот, вытащить и запомнить в переменной нужное. Продемонстрируем более экзотичный, первый вид.
Удаление ведущих и замыкающих пробельных символов:
$str =~ s/(?:^\s*|\s*$)//g;
Обратите внимание на модификатор "g". Без него будут удаляться только ведущие пробельные символы. В результате работы регулярного выражения, мы получим желаемое - наши данные, но без лишних ведущих и замыкающих пробельных символов.
Или, например, удалим все теги "<p>" из текста, вместе со всем содержимым, оставив только заголовки.
$str =~ s/<\s*p\s*>.*?<\s*\/\s*p\s*>//gis;
Что-то еще? А, ну да! Наши заголовки остались с окружаюищими тегами! Не нравится? Хотите просто тексты заголовок, да каждый в своей переменной? А может, еще и в хэш запомнить, чтобы ключом уровень заголовка (<h(уровень)>)? Пожалуйста!
%res = $str =~ /<\s*h(\d)\s*>(.*?)<\s*\/\s*h\1\s*>/gis;
Применяем один раз эту штуку и можно работать над созданием красивого "содержания" статьи. Хотя, для этой цели лучше, наверное, запоминать не в хэш, а в массив, чтобы избежать перепутывания записей. Для этого нужно изменить % на @ ;)
Не устали еще? Думаю, на сегодня хватит. Если знаете еще частовозникающие задачки на регулярные выражения - милости просим. Если наберу приличный сборник задач (и если эта статья придется по вкусу) - выпущу продолжение.
Учите регулярные выражения! Это сила. А если учить долго и лениво, а пользоваться нужно уже сейчас - закажите приложение у меня.