Разработчику. Сборник рецептов PHP
Задавайте вопросы

Выбор между поглощающим
и непоглощающим сравнением

Вернуться назад

Задача

Необходим шаблон для выделения наименьшей из возможных строк, а не наибольшей.

Решение

Добавьте символ ? после квантификатора, чтобы модифицировать конкретную часть шаблона:

// найти все части, выделенные полужирным шрифтом
preg_match_all('#<b>.+?</b>#', $html, $matches);

Или укажите в конце шаблона модификатор U, чтобы превратить все поглощающие квантификаторы в непоглощающие:

// найти все части, выделенные полужирным шрифтом
     preg_match_all('#<b>.+</b>#U', $html, $matches);

Обсуждение

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

Например, возьмем шаблон p.*, означающий p, а затем 0 или более символов, и применим его к строке php. Поглощающее регулярное выражение найдет одно совпадение, поскольку после захвата начального символа p оно продолжит работу и выделит также символы hp. С другой стороны, непоглощающее регулярное выражение найдет пару совпадений. Как и раньше, оно выделит символ p и символ h, но затем, вместо того чтобы продлить свое действие, оно прервется и оставит завершающий символ p непоглощенным. Следующая, вторая проверка выбирает последнюю букву.

Следующий фрагмент программы показывает, как поглощающее сравнение приводит только к одному успешному результату, а непоглощающее сравнение находит два совпадения:(*)

print preg_match_all('/p.*/', "php", $match); // поглощающее
print preg_match_all('/p.*?/', "php", $match); // непоглощающее
print preg_match_all('/p.*/U', "php", $match); // непоглощающее

1
2
2

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

Изначально все регулярные выражения были строго поглощающими. Поэтому нельзя использовать этот синтаксис с функциями ereg() или ereg_replace(). Поглощающее сравнение не поддерживается более старыми версиями механизма реализации функций регулярных выражений; вместо этого нужно использовать функции диалекта Perl.

Непоглощающее сравнение часто оказывается полезным при попытке выполнения упрощенного анализа HTML-документов. Предположим, что требуется найти весь текст между тегами полужирного шрифта. В случае применения поглощающего сравнения это будет выглядеть так:

$html = '<b>I am bold.</b> <i>I am italic.</i> <b>I am also bold.</b>';
preg_match_all('#<b>(.+)</b>#', $html, $bolds);
print_r($bolds[1]);

Array
(
       [0] => I am bold.</b> <i>I am italic.</i> <b>I am also bold.
)

Поскольку здесь есть второй набор тегов полужирного шрифта, шаблон продлит свое действие за пределы первого тега </b>, что сделает невозможным корректный анализ HTML. В случае же минимальной проверки оба набора тегов будут найдены по шаблону и замкнуты:

$html = '<b>I am bold.</b> <i>I am italic.</i> <b>I am also bold.</b>';
preg_match_all('#<b>(.+?)</b>#', $html, $bolds);
print_r($bolds[1]);

Array
(
       [0] => I am bold.
       [1] => I am also bold.
)

Конечно, из этого может ничего не получиться, если разметка не на 100% формально корректна и в ней присутствуют случайные теги полужирного шрифта.(**) Если единственной вашей целью является удаление всех (или некоторых) тегов HTML из блока текста, то лучше вообще обойтись без регулярных выражений. Вместо этого обратитесь к функции strip_tags(), – она работает быстрее и корректнее. Дополнительную информацию можно найти в рецепте (Удаление тегов HTML и PHP).

Заметим в заключение, что хотя идея непоглощающего сравнения пришла из языка Perl, сам модификатор -U не совместим с Perl и уникален именно для реализации Perl-диалекта регулярных выражений в PHP. Он инвертирует все квантификаторы, превращая их из поглощающих в непоглощающие, а также меняет направление их действия на обратное. Поэтому, чтобы применить поглощающий квантификатор внутри шаблона, находящегося в области действия завершающего модификатора /U, просто добавьте в конце квантификатора символ ? точно так же, как это обычно делается для превращения квантификатора из поглощающего в непоглощающий.


(*) В оригинале во всех трех примерах в записи функции preg_match_all() опущен третий обязательный параметр. В русском переводе код изменен, иначе интерпретатор выдаст предупреждение и не выполнит код примера.

(*) Даже имея формально корректный документ HTML, можно столкнуться с трудностями. Например, если теги полужирного шрифта находятся внутри комментария. Корректный HTML-анализатор проигнорирует эти разделы, но наш шаблон этого не сделает.

Вернуться назад

Рейтинг@Mail.ru

Яндекс.Метрика

Индекс цитирования

Рейтинг Сайтов ДОСКИ.РУ