Perl-совместимые регулярные выражения
В предыдущем разделе мы рассмотрели регулярные выражения POSIX. Кро- ме формата POSIX в языке PHP существует поддержка Perl-совместимых ре- гулярных выражений (PCRE, Perl-compatible Regular Expression). Именно формат PCRE следует использовать при работе с однобайтовыми кодировка- ми, а также с кодировкой UTF-8.
Шаблон PCRE представляет собой строку, заключенную в кавычки или апо- строфы, внутри которой между двумя ограничителями указывается регуляр- ное выражение. За последним ограничителем может быть указан модифика- тор. В качестве ограничителя могут служить одинаковые символы или парные скобки.
‘/<Регулярное выражение>/[<Модификатор>]’
‘#<Регулярное выражение>#[<Модификатор>]’
‘"<Регулярное выражение>"[<Модификатор>]’
‘{<Регулярное выражение>}[<Модификатор>]’
‘(<Регулярное выражение>)[<Модификатор>]’
В параметре <Модификатор> могут быть указаны следующие флаги (или их комбинация):
? i — поиск без учета регистра;
? m — поиск в строке, состоящей из нескольких строк, разделенных симво- лом новой строки. Символ ^ соответствует привязке к началу каждой подстроки, а символ $ соответствует позиции перед символом перевода строки. Метасимвол "точка" соответствует любому символу, кроме сим- вола перевода строки (\n);
? s — однострочный режим. Символ ^ соответствует привязке к началу строки, а символ $ соответствует концу строки. Метасимвол "точка" со- ответствует любому символу, в том числе и символу перевода строки;
? x — разрешает использовать в регулярном выражении пробельные сим-
волы и однострочные комментарии, начинающиеся с символа #;
? e — указывает, что в строке для замены в функции preg_replace() ука- зано выражение языка PHP, которое необходимо предварительно вы- числить. Вместо этого флага лучше использовать функцию preg_replace_callback();
? u — используется для обработки строк в кодировке UTF-8.
Метасимволы, используемые в регулярных выражениях PCRE:
? ^ — привязка к началу строки (назначение зависит от модификатора);
? $ — привязка к концу строки (назначение зависит от модификатора);
? \A — привязка к началу строки (не зависит от модификатора);
? \z — привязка к концу строки (не зависит от модификатора);
? [] — позволяет указать символы, которые могут встречаться на этом месте в строке. Можно перечислять символы подряд или указать диапа- зон через тире;
? [^] — позволяет указать символы, которые не могут встречаться на этом месте в строке. Можно перечислять символы подряд или указать диапа- зон через тире;
? n|m — соответствует одному из символов n или m;
? . (точка) — любой символ, кроме символа перевода строки (\n). Если используется модификатор s, то метасимвол "точка" соответствует всем символам, включая символ перевода строки. Внутри квадратных скобок точка не имеет специального значения.
Кроме того, в регулярных выражениях PCRE можно использовать следую-
щие стандартные классы:
? \d — соответствует любой цифре;
? \w — соответствует любой букве или цифре;
? \s — любой пробельный символ (пробел, табуляция, перевод страницы,
новая строка или перевод каретки);
? \D — не цифра;
? \W — не буква и не цифра;
? \S — не пробельный символ.
Также можно использовать стандартные классы регулярных выражений формата POSIX.
Квантификаторы, используемые в регулярных выражениях PCRE, позволяют задать число повторов предшествующего символа или выражения:
? {n} — n вхождений символа в строку;
? {n,} — n или более вхождений символа в строку;
? {n,m} — не менее n и не более m вхождений символа в строку. Числа ука-
зываются через запятую без пробела;
? * — ноль или большее число вхождений символа в строку;
? + — одно или большее число вхождений символа в строку;
? ? — ни одного или одно вхождение символа в строку.
Регулярные выражения PCRE можно использовать в нескольких функциях.
? preg_grep(<Шаблон>, <Массив>, [PREG_GREP_INVERT]) возвращает но-
вый массив, состоящий из элементов <Массива>, которые соответствуют
<Шаблону>. Индексы массива сохраняются. Если указан флаг PREG_GREP_INVERT, то возвращается массив значений, не соответствую- щих шаблону. В качестве примера получим все элементы массива, со- стоящие только из цифр, и наоборот:
$arr = array(20, 54, "Текст", 457);
$pattern = ‘/^[0-9]+$/s’;
$arr2 = preg_grep($pattern, $arr);
echo implode(" - ", $arr2);
// Выведет: 20 - 54 - 457 echo "<br>";
$arr3 = preg_grep($pattern, $arr, PREG_GREP_INVERT);
echo implode(" - ", $arr3);
// Выведет: Текст
? preg_match() ищет первое совпадение с шаблоном в заданной строке.
Функция имеет следующий формат:
preg_match(<Шаблон>, <Строка>, [<Массив совпадений>], [<Флаг>], [<Смещение от начала строки>]);
Функция возвращает 0, если совпадение не найдено, и 1 в случае соответ- ствия шаблону. Если указан параметр <Массив совпадений>, то первый элемент массива будет содержать фрагмент, полностью соответствующий шаблону, а остальные элементы массива — это фрагменты, заключенные в шаблоне в круглые скобки.
В качестве примера проверим E-mail на соответствие шаблону:
$emails = ‘[email protected]’;
$pattern = ‘/^([a-z0-9_.-]+)@(([a-z0-9-]+\.)+[a-z]{2,6})$/is’;
$arr = array();
if (preg_match($pattern, $emails, $arr)) {
echo ‘E-mail ‘ . $arr[0] . ‘ соответствует шаблону’;
echo ‘<br>ящик: ‘, $arr[1], ‘ домен: ‘, $arr[2];
}
else {
echo ‘E-mail не соответствует шаблону’;
}
Этот пример выведет код HTML, который отображается так:
E-mail [email protected] соответствует шаблону ящик: unicross домен: mail.ru
Элемент массива $arr[0] — это полный текст, соответствующий шабло- ну. Элемент $arr[1] соответствует ([a-z0-9_.-]+), а элемент $arr[2] соответствует шаблону во вторых круглых скобках (([a-z0-9-]+\.)+[a- z]{2,6}).
Если какой-либо фрагмент заносить в массив не надо, то после откры-
вающей круглой скобки следует указать комбинацию символов ?:.
Например,
$emails = ‘[email protected]’;
$pattern = ‘/^(?:[a-z0-9_.-]+)@(([a-z0-9-]+\.)+[a-z]{2,6})$/is’;
$arr = array();
if (preg_match($pattern, $emails, $arr)) {
echo ‘E-mail ‘ . $arr[0] . ‘ соответствует шаблону’;
echo ‘<br>домен: ‘, $arr[1];
}
else {
echo ‘E-mail не соответствует шаблону’;
}
выведет код, отображающийся в Web-браузере так: E-mail [email protected] соответствует шаблону домен: mail.ru
В этом примере элемент массива $arr[1] соответствует фрагменту уже не в первых круглых скобках, а во вторых.
Если в параметре <Флаг> указано значение PREG_OFFSET_CAPTURE, то для каждого найденного фрагмента будет указана его позиция в исходной строке. Для примера получим текст между одинаковыми парными тегами и выведем смещение относительно начала строки:
$str = "<b>Текст</b>";
$pattern = ‘#<([^>])>(.*)</\1>#s’;
$arr = array();
if (preg_match($pattern, $str, $arr, PREG_OFFSET_CAPTURE)) {
echo ‘Фрагмент: ‘, $arr[2][0], ‘<br>’;
echo ‘Смещение: ‘, $arr[2][1];
}
Этот пример использует механизм обратных ссылок. К найденному фрагменту в круглых скобках внутри шаблона можно обратиться, указав его порядковый номер после слэша, например, \1 соответствует ([^>]).
О БРАТИТЕ ВНИМ АНИЕ
При использовании флага PREG_OFFSET_CAPTURE изменился формат мас-
сива $arr:
$arr[0][0] => "<b>Текст</b>"
$arr[0][1] => 0
$arr[1][0] => "b"
$arr[1][1] => 1
$arr[2][0] => "Текст"
$arr[2][1] => 3
? preg_match_all() ищет все совпадения с шаблоном в заданной строке.
Функция имеет следующий формат:
preg_match_all(<Шаблон>, <Строка>, [<Массив совпадений>], [<Флаг>], [<Смещение от начала строки>]);
Функция возвращает количество найденных совпадений с шаблоном (или
0, если совпадения не найдены).
Если в параметре <Флаг> указано значение PREG_PATTERN_ORDER, то мас- сив совпадений будет содержать элементы, упорядоченные по порядко- вому номеру фрагмента, заключенного в шаблоне в круглые скобки. Ну-
левой элемент массива будет содержать список полных совпадений с шаблоном. В качестве примера получим все значения между тегами <b> и
</b>:
$str = "<b>Значение1</b>Лишнее значение<b>Значение2</b>";
$pattern = ‘#<b>(.*)</b>#is’;
$arr = array();
preg_match_all($pattern, $str, $arr, PREG_PATTERN_ORDER);
$count = count($arr[1]);
for ($i=0; $i<$count; $i++) {
echo $arr[1][$i] . "<br>";
}
Вместо желаемого результата мы получим
Значение1</b>Лишнее значение<b>Значение2<br>
Такое поведение квантификаторов называется "жадностью". При поиске соответствия ищется самая длинная подстрока, соответствующая шабло- ну, и не учитываются более короткие соответствия. В нашем случае са- мой длинной подстрокой является вся строка. Чтобы ограничить эту "жадность", необходимо после символа * указать символ ?.
Если в параметре <Флаг> указано значение PREG_SET_ORDER, то массив совпадений будет содержать элементы, упорядоченные по номеру совпа- дения. Каждый элемент массива будет содержать список совпадений фрагментов, заключенных в шаблоне в круглые скобки. Нулевой элемент массива будет содержать полное совпадение с шаблоном. В качестве примера получим все значения между тегами <b> и </b> с учетом "жад- ности" квантификаторов:
$str = "<b>Значение1</b>Лишнее значение<b>Значение2</b>";
$pattern = ‘#<b>(.*?)</b>#is’;
$arr = array();
preg_match_all($pattern, $str, $arr, PREG_SET_ORDER);
$count = count($arr);
for ($i=0; $i<$count; $i++) {
echo $arr[$i][1] . "<br>";
}
Этот код выведет то, что мы искали:
Значение1<br>Значение2<br>
Ограничить "жадность" всех квантификаторов в шаблоне позволяет мо- дификатор U. Обратите внимание на регистр модификатора. Буква долж- на быть прописной:
$pattern = ‘#<b>(.*)</b>#isU’;
Если к флагам PREG_PATTERN_ORDER и PREG_SET_ORDER добавить значение PREG_OFFSET_CAPTURE, то для каждого найденного фрагмента будет ука- зана его позиция в исходной строке:
$str = "<b>Значение1</b>Лишнее значение<b>Значение2</b>";
$pattern = ‘#<b>(.*?)</b>#is’;
$arr = array();
preg_match_all($pattern, $str, $arr,
PREG_SET_ORDER | PREG_OFFSET_CAPTURE);
echo "<pre>"; print_r($arr); echo "</pre>";
Код HTML, выведенный в этом примере, будет отображен в Web-
браузере так:
Array
(
[0] => Array
(
[0] => Array
(
[0] => Значение1 [1] => 0
)
[1] => Array
(
)
)
[1] => Array
(
[0] => Значение1 [1] => 3
[0] => Array
(
[0] => Значение2
[1] => 31
)
[1] => Array
(
[0] => Значение2 [1] => 34
)
)
)
Здесь основной вывод был осуществлен функцией print_r(), предназна-
ченной для вывода массивов, в том числе вложенных.
О БРАТИТЕ ВНИМ АНИЕ
Функция preg_match_all() показывает смещение в байтах, а не в симво-
лах. При использовании кодировки UTF-8 это будет иметь значение.
? preg_replace() ищет все совпадения с шаблоном и заменяет их указан-
ным значением. Функция имеет следующий формат:
preg_replace(<Шаблон>, <Новый фрагмент>, <Исходная строка>, [<Лимит>]);
Функция возвращает измененную строку. Если совпадения не найдены, то функция вернет исходную строку. Первые три параметра могут быть одномерными массивами. Если указан параметр <Лимит>, то функция за- менит только указанное количество первых совпадений с шаблоном.
В качестве примера возьмем два тега и поменяем имена тегов местами:
$str = "<br><td>";
$pattern = ‘#<(\w+)><(\w+)>#is’;
$repl = ‘<$2><$1>’;
$str2 = preg_replace($pattern, $repl, $str);
echo htmlspecialchars($str2);
// Выведет в окне Web-браузера: <td><br>
Обратиться к найденному фрагменту в круглых скобках можно не только с помощью синтаксиса $n, но и указав номер скобок, перед которым сто- ят два слэша (\\n):
$str = "<br><td>";
$pattern = ‘#<(\w+)><(\w+)>#is’;
$repl = ‘<\\2><\\1>’;
$str2 = preg_replace($pattern, $repl, $str);
echo htmlspecialchars($str2);
// Выведет в окне Web-браузера: <td><br>
Чтобы отделить номер скобки от последующего текста, необходимо за-
ключить номер в фигурные скобки (\${2}).
Если в шаблоне указан флаг e, то внутри выражения для замены можно использовать выражения языка PHP. В качестве примера поменяем теги местами и выведем названия тегов строчными буквами:
$str = "<BR><TD>";
$pattern = ‘#<(\w+)><(\w+)>#ise’;
$repl = "'<‘ . strtolower(‘$2’) . ‘><‘ . strtolower(‘$1’) .
‘>’";
$str2 = preg_replace($pattern, $repl, $str);
echo htmlspecialchars($str2);
// Выведет в окне Web-браузера: <td><br>
О БРАТИТЕ ВНИМ АНИЕ
Вместо этого способа замены лучше воспользоваться функцией preg_replace_callback(). Все дело в том, что переменные $1, $2 и т. д. перед вставкой в строку автоматически обрабатываются функцией addslashes(). Если в этих переменных содержатся кавычки и апострофы, то в итоговой строке обязательно будут лишние слэши. По этой причине лучше отказаться от использования флага e.
? preg_replace_callback() выполняет поиск по шаблону и замену с ис- пользованием функции обратного вызова. Функция имеет следующий формат:
preg_replace_callback(<Шаблон>, <Имя функции>,
<Строка для замены >, [<Лимит>]);
В отличие от preg_replace() функция preg_replace_callback() передает функции, указанной в параметре <Имя функции>, найденные совпадения. Результат, возвращаемый этой функцией, служит фрагментом для замены.
Переделаем наш предыдущий пример и используем функцию обратного вызова:
$str = "<BR><TD>";
$pattern = ‘#<(\w+)><(\w+)>#is’;
$str2 = preg_replace_callback($pattern, "f_replace", $str);
echo htmlspecialchars($str2);
// Выведет в окне Web-браузера: <td><br>
function f_replace($arr) {
$repl = ‘<‘ . strtolower($arr[2]);
$repl .= ‘><‘ . strtolower($arr[1]) . ‘>’;
return $repl;
}
Нулевой элемент массива $arr будет содержать полное соответствие шаблону, а последующие элементы соответствуют фрагментам, заклю- ченным в шаблоне в круглые скобки.
? preg_split() разбивает строку по шаблону и возвращает массив под-
строк. Функция имеет следующий формат:
preg_split(<Шаблон>, <Исходная строка>, [<Лимит>], [<Флаг>]);
В параметре <Флаг> могут быть указаны следующие значения (или ком-
бинация значений, соединенных оператором |):
• PREG_SPLIT_NO_EMPTY — функция вернет только непустые подстроки;
• PREG_SPLIT_DELIM_CAPTURE — фрагмент, заключенный в шаблоне в круглые скобки, также будет возвращаться;
• PREG_SPLIT_OFFSET_CAPTURE — для каждой найденной подстроки бу-
дет указана ее позиция в исходной строке.
Например, разбить E-mail на составные части можно так:
$str = ‘[email protected]’;
$arr = preg_split(‘/[@.]/’, $str);
$count = count($arr);
for ($i=0; $i<$count; $i++) {
echo $arr[$i] . "<br>";
} // Выведет unicross<br>mail<br>ru<br>
Если не требуется указания шаблона, то вместо функции preg_split()
лучше использовать функцию explode().
Источник: Прохоренок Н. А. HTML, JavaScript, PHP и MySQL. Джентльменский набор Web-мастера. — 3-е изд., перераб. и доп. — СПб.: БХВ-Петербург, 2010. — 912 с.: ил. + Видеокурс (на CD-ROM) — (Профессиональное программирование)