Регулярные выражения – это мощный инструмент сравнения и обработки текста. Они не так быстры, как обычные операции сравнения
строк, но отличаются чрезвычайной гибкостью, позволяя создавать
шаблоны сравнения для практически любой комбинации символов
при помощи довольно простого, хотя и немногословного и до некоторой степени трудного для понимания синтаксиса.
В PHP функции регулярных выражений применяются для поиска
текста, удовлетворяющего определенному критерию. После того как
нужный текст найден, можно заменять или модифицировать как весь
текст, так и его части – подстроки, соответствующие шаблону. Например, следующее регулярное выражение преобразует текстовые адреса
электронной почты в гиперссылки вида mailto::
$html = preg_replace('/[^@\s]+@([ -a-z0-9]+\.)+[a-z]{2,}/i',
'<a href="mailto:$0">$0</a>', $text);
Как видите, регулярные выражения довольно удобны для преобразования простого текста в форматHTML и наоборот. К счастью, эта тема столь популярна, что в PHP уже имеется множество встроенных
функций для решения таких задач. В рецепте-(Пользовательские данные и escape-последовательности) рассказано, как превращать сущности HTML в escape-последовательности, рецепт-(Удаление тегов HTML и PHP). посвящен удалению тегов HTML, а в рецептах (Преобразование ASCII в HTML) и (Преобразование HTML в ASCII) показано,
как конвертировать ASCII в HTML и, соответственно, HTML в ASCII.
Дополнительные сведения о поиске и проверке адресов электронной
почты посредством регулярных выражений можно почерпнуть из рецепта-(Поиск в файле всех строк,
соответствующих шаблону).
За несколько лет функциональное назначение регулярных выражений сильно расширилось по сравнению с их базовыми возможностями
и теперь включает в себя множество чрезвычайно полезных характеристик. В результате PHP в современном виде предлагает два различных набора функций для работы с регулярными выражениями. Первый набор включает традиционные (или POSIX) функции, которые начинаются с префикса ereg (от «extended regular expressions» – расширенные регулярные выражения; фактически функции ereg уже сами по себе являются расширением базового набора возможностей). Другой набор включает в себя функции Perl-диалекта регулярных выражений с префиксом preg (от «Perlcompatible regular expressions»–Perl-совместимые регулярные выражения).
Работа функций preg основана на библиотеке, имитирующей функциональность регулярных выражений языка программирования Perl. Это весьма удачное решение, поскольку Perl-диалект позволяет делать с помощью регулярных выражений массу полезных вещей, таких как непоглощающие сравнения, опережающие и ретроспективные проверки
и даже рекурсивные шаблоны.
По сути, нет причин и дальше использовать функции ereg. Они обладают меньшими возможностями и более медленны, чем функции preg.
Однако функции ereg существовали в PHP задолго до введения функций preg, поэтому многие программисты все еще применяют их из-за
того, что приходится иметь дело с унаследованными программами,
или в силу привычки. К счастью, прототипы функций двух этих наборов идентичны, поэтому можно без труда переключаться от одного набора к другому и обратно, не внося слишком большой путаницы в код.
(В рецепте-(Переход от ereg к preg) мы покажем, как это делается, пропуская наиболее
очевидные моменты.)
Основы регулярных выражений довольно просты для понимания. Последовательность символов заключается в шаблон. Затем строки текста сравниваются с шаблоном и определяются совпадения. Большинство символов в шаблоне представляют сами себя. Поэтому для того
чтобы определить, содержит ли строка HTML тег изображения, надо сделать следующее:
if (preg_match('/<img /', $html)) {
// найден открывающий тег изображения
}
Функция preg_match() сравнивает шаблон <"img " с содержимым переменной $html. Если она находит совпадение, то возвращает значение 1;в противном случае возвращается 0. Символы / называются разделителями шаблона; они определяют начало и конец шаблона.
Есть и несколько специальных символов. Причем именно их специальная природа расширяет возможности регулярных выражений по
сравнению с такими функциями, как strstr() и strpos().Такие символы называют метасимволами. К наиболее часто используемым метасимволам относятся точка (.), звездочка (*)>, плюс (+) и знак вопроса (?).
Соответственно, чтобы задать в шаблоне поиск символа, соответствующего метасимволу, необходимо поставить перед символом обратную
косую черту (\).
• Точка соответствует любому символу, поэтому шаблон /.at/ будет
совпадать с bat, cat и даже с rat.
• Звездочка означает 0 или более повторений предшествующих объектов. (В настоящий момент единственными объектами, о которых
нам известно, являются символы.)
• Действие плюса подобно действию звездочки, но означает 1 или более повторений, а не 0 или более. Поэтому /.+at/ соответствует brat, sprat и даже catastrophe, но не at. Чтобы найти at, нужно заменить + на *.
• Знак вопроса означает 0 или 1 объект.
Чтобы применить * и + к объектам, состоящим более чем из одного символа, заключите эту последовательность символов в круглые скобки.
Круглые скобки позволяют оперировать группами символов для более
сложного поиска и сохраняют часть шаблона, заключенную в них.
На выделенную последовательность можно затем ссылаться в функции preg_replace() для замены строки, и все совпадения, выделенные таким способом, могут быть сохранены в массиве, передаваемом в качестве третьего параметра в функции preg_match() и preg_match_all().
Функция preg_match_all() действует подобно функции preg_match(), но
находит все возможные совпадения внутри строки, а не останавливается на первом. Приведем несколько примеров:
if (preg_match('/<title>.+<\/title>/', $html)) {
// страница имеет название
}
if (preg_match_all('/<li>/', $html, $matches)) {
print 'Page has ' . count($matches[0]) . " list items\n";
}
// превращаем полужирный шрифт в курсив
$italics = preg_replace('/(<\/?)b(>)/', '$1i$2', $bold);
Если вы хотите найти строки, состоящие из определенного набора букв, создайте символьный класс из этих букв. Символьный класс представляет собой последовательность символов, заключенную в квадратные скобки. Знак вставки (^) и знак доллара ($) связывают шаблон с началом и концом строки соответственно. Без их указания поиск совпадений может происходить в любом месте строки. Поэтому, для того чтобы найти все гласные в строке, надо создать символьный класс, содержащий a, e, i, o и u, начать шаблон с символа ^ и закончить его символом $:
preg_match('/^[aeiou]+$/', $string); // только гласные
Если искомое проще определить с помощью дополнения, можно так и сделать. Чтобы построить символьный класс, определяющий дополнение своего содержимого, укажите в его начале символ вставки. Если указанный снаружи символьного класса символ вставки привязывает шаблон к началу строки, то символ вставки внутри символьного класса означает «совпадение со всем, кроме того, что перечислено в квадратных скобках»:
preg_match('/^[^aeiou]+$/', $string) // только не гласные
Обратите внимание, противоположность [aeiou] – это не только [bcdfghjklmnpqrstvwxyz]. Символьный класс [^aeiou] также соответствует
гласным в верхнем регистре, таким как AEIOU, числам, например 123,
URL, таким как http://www.cnpq.br/, и даже смайликам, например :).
Вертикальная черта (|), используемая также для указания канала, задает указание альтернативы, например:
// находим gif или jpeg
preg_match('/(gif|jpeg)/', $images);
Кроме рассмотренных выше, существует еще одна группа метасимволов. Они действуют схожим образом, но соответствуют не одному, а нескольким символам. Полезными метасимволами из этой группы являются: \w (соответствует любому текстовому символу, [a-zA-Z0-9_]);(*) \d(соответствует любой цифре, [0 9]); \s (соответствует любому пробельному символу) и \b (соответствует границе слова).(**) Ниже показано,как найти все числа, которые не являются частью другого слова:
// находим цифры, не соприкасающиеся с другими словами
preg_match_all('/\b\d+\b/', $html, $matches);
Это соответствует 123, 76! и 38-years-old, но не 2nd.
Приведем шаблон, представляющий собой регулярное выражение,
эквивалентное функции trim():
// удаляем начальные или заключительные пробельные символы
$trimmed = preg_replace('/(^\s+)|(\s+$)/', '', $string);
Наконец, поговорим о модификаторах шаблона. Модификаторы влияют на весь шаблон, а не только на отдельный символ или группу символов. Модификаторы шаблона располагаются после закрывающего разделителя шаблона. Например, буква i делает регулярное выражение в шаблоне не чувствительным к регистру:
// точное совпадение только с тегами изображения
// в нижнем регистре (допускается в XHTML)
if (preg_match('/<img[^>]+>/', $html)) {
...
}
// соответствует тегам изображения и в верхнем, и в нижнем регистре
if (preg_match('/<img[^>]+>/i', $html)) {
...
}
Мы затронули лишь небольшое подмножество регулярных выражений. Некоторые дополнительные аспекты мы рассмотрим в следующих рецептах, а на веб-сайте PHP, по адресу http://www.php.net/regex
можно найти очень полезную информацию о регулярных выражениях POSIX, тогда как регулярные выражения, поддерживающие диалект Perl, рассмотрены по адресу http://www.php.net/pcre. На этой последней странице приведены также ссылки на очень подробные и информативные разделы «Pattern Modifiers» (Модификаторы шаблонов) и «Pattern Syntax» (Синтаксис шаблонов).
(*) В оригинале – «any word character», что можно перевести как «любой текстовый символ». Данная нотация заимствована из языка Perl. Фактически множество символов \w трактуется в нем как множество символов, допустимых в идентификаторах (именах переменных). Это изначально соответствует символам английского алфавита в верхнем и нижнем регистрах, цифрам и символу подчеркивания. Это множество принято также называть «символами слова».
(**) Несмотря на название, в действительности символ \b определяется как позиция между символами, по одну сторону от которой расположены символы множества \w, а по другую – символы дополнения этого множества. Например, в результате вычисления функции preg_match_all("/(\b\w+\b)/", "38-years-old", $match) массив $match получит значение ("38", "years", "old")