1. Главная страница » Компьютеры » Php регулярные выражения жадность

Php регулярные выражения жадность

Автор: | 16.12.2019

Я когда-то писал о квантификаторах повторений. Так вот, мы с Вами там рассмотрели только "жадных" представителей. Однако, не всегда их "жадность" нам помогает, очень часто она и вредит. Чуть ниже я покажу пример, где чётко видно то, что они иногда делают, а также покажу как уменьшить их пыл и сделать квантификаторы повторений "ленивыми".

Давайте разберём простой и уже ставший классическим пример:

/i", "здесь_жирное_выделение", $str);
echo $result_str;
?>

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

Вот как надо писать данный код, чтобы замена происходила так, как мы хотим:

/i", "здесь_жирное_выделение", $str);
echo $result_str;
?>

Всё, что мы изменили это регулярное выражение, поставив после квантификатора повторений ".*" знак вопроса, который как раз и делает квантификатор "ленивым". Теперь результатом будет: "Небольшая здесь_жирное_выделение c здесь_жирное_выделение выделением.", чего мы и пытались добиться.

Я очень надеюсь, что Вы осознали проблему "жадности" квантификаторов и поняли, как они работают. А также осознали, как это можно исправить простым знаком "?". Эта тема действительно важная, и огромное количество новичков в регулярных выражениях делают одни и те же ошибки, связанные с "жадностью" квантификаторов повторений, поэтому если Вы ещё не осознали весь смысл до конца, то перечитайте статью ещё раз.

Копирование материалов разрешается только с указанием автора (Михаил Русаков) и индексируемой прямой ссылкой на сайт (http://myrusakov.ru)!

Добавляйтесь ко мне в друзья ВКонтакте: http://vk.com/myrusakov.
Если Вы хотите дать оценку мне и моей работе, то напишите её в моей группе: http://vk.com/rusakovmy.

Если Вы не хотите пропустить новые материалы на сайте,
то Вы можете подписаться на обновления: Подписаться на обновления

Если у Вас остались какие-либо вопросы, либо у Вас есть желание высказаться по поводу этой статьи, то Вы можете оставить свой комментарий внизу страницы.

Порекомендуйте эту статью друзьям:

Если Вам понравился сайт, то разместите ссылку на него (у себя на сайте, на форуме, в контакте):

Она выглядит вот так:

  • BB-код ссылки для форумов (например, можете поставить её в подписи):
  • Комментарии ( 0 ):

    Для добавления комментариев надо войти в систему.
    Если Вы ещё не зарегистрированы на сайте, то сначала зарегистрируйтесь.

    Copyright © 2010-2019 Русаков Михаил Юрьевич. Все права защищены.

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

    Правила составление шаблона (pattern)

    Границы шаблона должны обозначаться определенными символами, часто используют "/", но я предпочитаю использовать "#" потому, что от обилия прямых/обратных слешей может в глазах зарябить, а "решетки" обычно больше нигде не используются. Итак: "#ТутТелоРегулярногоВыражения#"

    Внутри регулярного выражения используются скобки — это подвыражения, с которыми можно производить манипуляции, пример:

    Это выражение предназначено для получения параметров в строке URL. В начале строки идет спецсимвол "^" — это означает начало строки. Далее идет "/catalog/" — тут нет спецсимволов, это просто текст, который должен содержатся в строке. Затем встретили круглые скобки, т.е. дошли до первого подвыражения. В квадратных скобках обозначаются множество символов, которые могут быть в строке в этом месте. Знак "" означает перечисление. Знак "" экранирует специальные символы. Таким образом, в первом подвыражении у нас могут идти БОЛЬШИЕ и маленькие буквы латинского алфавита, цифры от 0 до 9, знак подчеркивания, тире и точка. Тире и точка — это спецсимволы, но здесь они экранированы, поэтому здесь это просто символы. После квадратных скобок идет знак "+" — это значит предыдущий символ (а у нас это множество символов заданное в квадратных скобках) может идти 1 или больше раз. Затем идет "/" — это просто символ, и аналогичное второе подвыражение. Затем идет ".html", что означает текст ".html". А затем спец символы ".*" точка означает любой символ, а звездочка любое количество предыдущего символа. Т.е. после ".html" может идти все что угодно.

    Читайте также:  Papa johns новосибирск промокод

    Указание количества, квантификаторы

    Выше мы уже рассмотрели такие символы, указывающие количество предыдущих символов, как + и *. Приведем все возможности указания количества:

    , где Х это число повторений, пример: [0-9] , это значит что здесь должно быть 5 цифр
    , где X минимальное количество, Y — максимальное количество повторений. Пример [0-9] — 1 или 2 цифры. [0-9] -0, 1 или 2 цифры. [0-9] — 2 и более цифр
    * любое количество символов (включая 0 символов)
    + 1 и более символов
    ? 1 или 0 символов

    Спецсимволы

    экранирование спецсимвола
    \ обратный слеш
    | Метасимвол выбора (или или)
    ^ Метасимвол начала строки
    $ Метасимвол конца строки

    Символ перевода строки (шестнадцатеричный код 0x0A)

    Символ возврата каретки (шестнадцатеричный код 0x0D)
    Символ табуляции (шестнадцатеричный код 0x09)
    . Точка. Любой символ.

    Для некоторых групп символов есть специальные сокращения:

    d Цифра (0-9), вместо [0-9]
    D Не цифра (любой символ кроме символов 0-9)
    s Пустой символ (обычно пробел и символ табуляции)
    S Непустой символ (все, кроме символов, определяемых метасимволом s)
    w "Словарный" символ (символ, который используется в словах. Обычно все буквы, все цифры и знак подчеркивания (‘_‘))
    W Все, кроме символов, определяемых метасимволом w

    "Жадность"

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

    "сейчас будет ссылка ссылка1, и еще ссылка2, итого 2 ссылки."

    Нужно получить ссылки, для этого составили выражение:

    Вроди все верно, подвыражение подходит под:

    Но оно также подходит под:

    — его то мы и получим, т.к. регулярные выражения по умолчанию "жадные". Снять жадность можно с помощью модификатора "U", вот так:

    Модификаторы

    После регулярного выражения могут идти модификаторы: "#ТутТелоРегулярногоВыражения#ТутМодификаторы" Виды модификаторов:

    i Включает режим case-insensitive, т.е. большие и маленькие буквы в выражении не различаются.
    m Указывает на то, что текст, по которому ведется поиск, должен рассматриваться как состоящий из нескольких строк. По умолчанию механизм регулярных выражений рассматривает текст как одну строку вне зависимости от того, чем она является на самом деле. Соответственно метасимволы ‘^’ и ‘$’ указывают на начало и конец всего текста. Если же этот модификатор указан, то они будут указывать соответственно на начало и конец каждой строки текста.
    s По умолчанию метасимвол ‘.‘ не включает в свое определение символ перевода строки. Указание этого модификатора снимает это ограничение.
    U Снимает жадность регулярного выражения
    u Включает работу регулярных выражений с кириллицей в UTF-8, иначе работает не корректно.

    php Функции для работы с регулярными выражениями

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

    preg_match

    Выполняет проверку на соответствие регулярному выражению:

    preg_match_all

    Выполняет глобальный поиск шаблона в строке:

    Возвращает количество найденных вхождений шаблона (которое может быть и нулем) либо FALSE, если во время выполнения возникли какие-либо ошибки. В переменную $res попадет результат поиска в виде массива: [0] содержит массив полных вхождений шаблона, элемент [1] содержит массив вхождений первой подмаски, и так далее. Для удобства работы с регулярными выражениями я создал страницу, где в режиме online можно поработать с функцией preg_match_all

    preg_replace

    Каждое значение может быть строкой или массивом, в случае, если $subject массив — возвращается массив, иначе строка

    Читайте также:  Digital remote control switch b 3 схема

    preg_split

    Разбивает строку по регулярному выражению:

    Возвращает массив, состоящий из подстрок заданной строки subject, которая разбита по границам, соответствующим шаблону pattern.

    На первый взгляд квантификаторы – это просто, но на самом деле это не совсем так.

    Нужно очень хорошо разбираться, как работает поиск, если планируем искать что-то сложнее, чем /d+/ .

    Давайте в качестве примера рассмотрим следующую задачу:

    У нас есть текст, в котором нужно заменить все кавычки ". " на «ёлочки» «. » , которые используются в типографике многих стран.

    Например: "Привет, мир" должно превратиться в «Привет, мир» . Есть и другие кавычки, вроде „Witam, świat!” (польский язык) или 「你好,世界」 (китайский язык), но для нашей задачи давайте выберем «. » .

    Первое, что нам нужно – это найти строки с кавычками, а затем мы сможем их заменить.

    Регулярное выражение вроде /".+"/g (кавычка, какой-то текст, другая кавычка) может выглядеть хорошим решением, но это не так!

    Давайте это проверим:

    …Как мы видим, регулярное выражение работает не как задумано!

    Вместо того, чтобы найти два совпадения "witch" и "broom" , было найдено одно: "witch" and her "broom" .

    Причину можно описать, как «жадность – причина всех зол».

    Жадный поиск

    Чтобы найти совпадение, движок регулярных выражений работает по следующему алгоритму:

    • Для каждой позиции в строке для поиска:
    • Попробовать найти совпадение с шаблоном на этой позиции.
    • Если нет совпадения, переход к следующей позиции.

    Эти общие слова никак не объясняют, почему регулярное выражение работает неправильно, так что давайте разберём подробно, как работает шаблон ".+" .

    Первый символ шаблона – это кавычка " .

    Движок регулярного выражения пытается найти его на нулевой позиции исходной строки a "witch" and her "broom" is one , но там – a , так что совпадения нет.

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

    Кавычка замечена, после чего движок пытается найти совпадение для оставшегося шаблона. Смотрит, удовлетворяет ли остаток строки шаблону .+" .

    В нашем случае следующий символ шаблона: . (точка). Она обозначает «любой символ, кроме новой строки», так что следующая буква строки ‘w’ подходит.

    Затем точка повторяется из-за квантификатора .+ . Движок регулярного выражения добавляет к совпадению один символ за другим.

    …До каких пор? Точке соответствуют любые символы, так что движок остановится только тогда, когда достигнет конца строки:

    Тогда он перестанет повторять .+ и попробует найти следующий символ шаблона. Это кавычка " . Но есть проблема: строка для поиска закончилась, больше нет символов!

    Движок регулярного выражения понимает, что захватил слишком много .+ и начинает отступать.

    Другими словами, он сокращает совпадение по квантификатору на один символ:

    Теперь он предполагает, что .+ заканчивается за один символ до конца строки и пытается сопоставить остаток шаблона для этой позиции.

    Если бы тут была кавычка, тогда бы поиск закончился, но последний символ – это ‘e’ , так что он не подходит.

    …Поэтому движок уменьшает количество повторений .+ ещё на один символ:

    Кавычка ‘"’ не соответствует ‘n’ .

    Движок продолжает возвращаться: он уменьшает количество повторений ‘.’ , пока оставшийся шаблон (в нашем случае ‘"’ ) не совпадёт:

    Так что первое совпадение: "witch" and her "broom" . Если у регулярного выражения стоит флаг g , то поиск продолжится с того места, где закончился предыдущий. В оставшейся строке is one нет кавычек, так что совпадений больше не будет.

    Это, определённо, не то, что мы ожидали. Но так оно работает.

    В жадном режиме (по умолчанию) квантификатор повторяется столько раз, сколько это возможно.

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

    В нашей задаче мы хотим другого. И нам поможет ленивый режим квантификатора.

    Ленивый режим

    «Ленивый» режим противоположен «жадному». Он означает: «повторять квантификатор наименьшее количество раз».

    Читайте также:  360 Total security тесты

    Мы можем включить его, вставив знак вопроса ‘?’ после квантификатора, то есть будет *? или +? или даже ?? для ‘?’ .

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

    Регулярное выражение /".+?"/g работает как задумано, оно находит "witch" и "broom" :

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

    Первый шаг будет таким же: движок находит начало шаблона ‘"’ на 3-ей позиции:

    Следующий шаг аналогичен: он найдёт совпадение для точки ‘.’ :

    А отсюда поиск продолжится по-другому. Из-за того, что у нас включён ленивый режим для +? , движок не будет пытаться найти совпадение для точки ещё раз, оно остановится и попробует найти совпадение для оставшегося шаблона ‘"’ прямо сейчас:

    Если бы на этом месте была кавычка, то поиск бы закончился, но там находится ‘i’ , то есть совпадения нет.

    Тогда движок регулярного выражения увеличит количество повторений для точки и попробует ещё раз:

    Опять неудача. Тогда количество повторений будет увеличено ещё и ещё…

    …До тех пор, пока совпадение для оставшегося шаблона не будет найдено:

    Следующий поиск начнётся с того места, где закончилось текущее совпадение и у нас будет ещё один результат:

    В этом примере мы увидели, как ленивый режим работает для +? . Квантификаторы +? и ?? работают аналогичным образом – движок регулярного выражения увеличит количество совпадений, только если не сможет найти совпадение для оставшегося шаблона на текущей позиции.

    Ленивый режим включается только для квантификаторов с ? .

    Остальные квантификаторы остаются жадными.

    Шаблон d+ пытается найти столько цифр, сколько возможно (жадный режим), так что он находит 123 и останавливается, потому что следующим символом будет пробел ‘ ‘ .

    Дальше в шаблоне пробел и в строке тоже, так что есть совпадение.

    Затем идёт d+? . Квантификатор находится в ленивом режиме, так что он находит одну цифру 4 и проверяет, есть ли совпадение для оставшегося шаблона с этого места.

    …Но в шаблоне d+? больше ничего нет.

    Ленивый режим ничего не повторяет без необходимости. Шаблон закончился, заканчивается и поиск. Мы получаем 123 4 .

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

    Но эти внутренние оптимизации для нас незаметны, снаружи всё будет работать, как описано.

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

    Альтернативный подход

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

    В нашем случаем мы можем найти кавычки без использования ленивого режима с помощью регулярного выражения "[^"]+" :

    Регулярное выражение "[^"]+" получит нужный результат, потому что оно ищет кавычку ‘"’ , за которой следует один или несколько символов «не-кавычек» [^"] , а затем – закрывающая кавычка.

    Движок регулярного выражения набирает, сколько может, [^"]+ , пока не встречает закрывающую кавычку, на которой останавливается.

    Обратите внимание, что эта логика не заменяет ленивые квантификаторы!

    Просто она работает по-другому. Временами нужен один вариант, временами – другой.

    Давайте посмотрим пример, в котором ленивый квантификатор не справляется, а этот вариант работает правильно.

    Например, мы хотим найти ссылки вида , с произвольным href .

    Какое регулярное выражение нам нужно использовать?

    Первой мыслью может быть: //g .

    Регулярное выражение работает. Но давайте посмотрим, что произойдёт, если в тексте будет много ссылок?

    В данном случае мы получили неправильный результат по той же причине, что в примере с «witches». Квантификатор .* забирает слишком много символов.

    Совпадение будет выглядеть так:

    Давайте изменим шаблон, сделав квантификатор ленивым .*? :

    Добавить комментарий

    Ваш e-mail не будет опубликован. Обязательные поля помечены *

    *

    code