Рубрики
Без рубрики

Python Regex жадные против не жадных квантов

Готовы заработать черный ремень твоего перестройки сверхдержавы? В этом руководстве показано тонкая, но важная разница между жадными и не жадными квантами Regex. Вы также можете посмотреть видео, которое я создал для этой статьи: https://youtu.be/srtn0fxu0vq Если вы заняты, вот короткая версия этого руководства: определение жадный квантификатор: жадный … Python Regex Greedy vs -Грейные квантифиры Подробнее »

Автор оригинала: Chris.

Готовы заработать черный ремень твоего перестройки сверхдержавы? В этом руководстве показано тонкая, но важная разница между жадными и не жадными квантами Regex.

Вы также можете посмотреть видео объяснения, которое я создал для этой статьи:

Если вы заняты, вот короткая версия этого руководства:

Определение жадный квантификатор:

Жадный квантификатор, такой как ? , * , + С и {m, n} Соответствует как можно больше персонажей (самый длинный матч). Например, Regex ‘a+’ будет соответствовать как много ‘a’ S, насколько это возможно в вашей строке «АААА» – Несмотря на то, что подстроки ‘a’ , «АА» , «ААА» Все соответствуют Regex ‘a+’ .

Определение не жадного квантификатора:

Не жадный квантификатор, такой как ?? , *? , +? С и {m, n}? Матч как можно меньше персонажей (кратчайший возможный матч). Например, Regex « A +? будет соответствовать немногим ‘a’ S, насколько это возможно в вашей строке «АААА» Отказ Таким образом, он соответствует первым персонажем ‘a’ И сделано с этим.

Инженеры Google, Facebook и Amazon являются регулярными мастерами. Если вы хотите стать одним, а также проверить нашу новую книгу: Самый умный способ изучить Python Regex (Amazon Kindle/Print, открывается на новой вкладке) Отказ

Но первые вещи первыми: Какие «количественные кванты» в любом случае? Отличный вопрос – я рад, что вы спросили! Итак, давайте погрузимся в три основных количественных регенерация Python.

Python Regex Quebifiers

Слово « квантификатор » происходит от латыни: это значение Кванс много/Как часто Отказ

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

Связанная статья: Python Regex SuperPower – Ultimate Guide

Если вы считаете, что вы не определяете какой-либо квантификатор, вы делаете это неявно: никакого количества означает совпадение регулярного выражения ровно.

Так Каковы квантования Regeex в Python?

Квантификатор Значение
А? Сопоставить регулярное выражение ноль или один раз
A* Сопоставить регулярное выражение нуля или более раз
A+ Сопоставить регулярное выражение единое или несколько раз
Являюсь} Соответствовать регулярному выражению точно M раз
A {m, n} Соответствовать регулярному выражению между m и n раз (включая)

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

Вы хотите освоить сверхдержаву Regeex? Проверьте мою новую книгу Самый умный способ изучать регулярные выражения в Python С инновационным 3-ступенчатым подходом для активного обучения: (1) Изучение книги главы, (2) Решите головоломки кода и (3) Смотреть воспроизведение главы видео.

Вы видите в таблице, что квантаторы ? С * С + , {м} и {m, n} Определите, как часто вы повторяете сопоставление Regex А Отказ

Давайте посмотрим на некоторые примеры – один для каждого квантификатора:

>>> import re
>>> re.findall('a?', 'aaaa')
['a', 'a', 'a', 'a', '']
>>> re.findall('a*', 'aaaa')
['aaaa', '']
>>> re.findall('a+', 'aaaa')
['aaaa']
>>> re.findall('a{3}', 'aaaa')
['aaa']
>>> re.findall('a{1,2}', 'aaaa')
['aa', 'aa']

В каждой строке вы попробуйте другой квантификатор на одном тексте «АААА» Отказ И, интересно, каждая строка приводит к другому выходу:

  • нулевой или один Regex 'а? соответствует четыре раза один «А» Отказ Обратите внимание, что он не соответствует нулевым символам, если он может избежать этого.
  • ноль или-другое Regex « A * ' совпадает раз в четырех «А» и поглощает их. В конце строки он все еще может сравниться с пустой строкой.
  • одно- или больше Regex 'A +' совпадает раз в четырех «А» с. В отличие от предыдущего квантификатора, он не может сравниться с пустой строкой.
  • Повторяющееся регеекс 'a {3}' соответствует до трех «А» S за один пробег. Это может сделать это только один раз.
  • Повторяющееся регеекс 'a {1,2}' соответствует одному или два «А» с. Он пытается соответствовать как можно больше.

Вы узнали основные кванты регулярных выражений Python. Теперь пришло время изучить смысл срока жадности. А не ___ ли нам?

Python Regex жадный матч

Жадный матч означает, что Regeex Engine (тот, который пытается найти свой шаблон в строке), соответствует максимальному количеству символов.

Например, Regex 'A +' будет соответствовать как много «А» S, насколько это возможно в вашей строке «АААА» Отказ Хотя подстроки «А» , «АА» , «ААА» Все соответствуют регелесу 'A +' Это недостаточно для двигателя Regex. Это всегда голоден и пытается совпадать еще больше.

Другими словами, жадные кванты дают вам самый длинный матч из данного положения в строке.

Как оказывается, все кванты по умолчанию ? С * С + , {м} и {m, n} Вы узнали выше, являются жадными: они «потребляют» или соответствуют максимальному максимальному символам, так что рисунок Regex по-прежнему удовлетворен.

Вот вышеуказанные примеры снова, что все показывают, насколько жадным двигатель Regex:

>>> import re
>>> re.findall('a?', 'aaaa')
['a', 'a', 'a', 'a', '']
>>> re.findall('a*', 'aaaa')
['aaaa', '']
>>> re.findall('a+', 'aaaa')
['aaaa']
>>> re.findall('a{3}', 'aaaa')
['aaa']
>>> re.findall('a{1,2}', 'aaaa')
['aa', 'aa']

Во всех случаях более короткий матч также будет действительным. Но поскольку двигатель Regex жадный по умолчанию, тех, кто недостаточно для двигателя Regex.

Хорошо, так как мы можем сделать не жадный матч?

Python Regex не жадный матч

Не жадный матч означает, что двигатель Regex соответствует максимальному количеству символов – так что он все еще может сопоставить шаблон в данной строке.

Например, Regex «А +?» будет соответствовать немногим «А» S, насколько это возможно в вашей строке «АААА» Отказ Таким образом, он соответствует первым персонаже «А» И сделано с этим. Затем он движется ко второму символу (который также является матчом) и так далее.

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

Вы можете сделать кванты по умолчанию ? С * С + , {м} и {m, n} не жадный, добавляя знак вопросительного знака '? К ним: ?? , *? , +? и {m, n}? Отказ Они «потребляют» или соответствуют максимальному количеству символов, так что шаблон Regeex по-прежнему удовлетворен.

Вот несколько примеров, которые показывают, как не жадные подходящие работы:

Не жадный вопрос Оператор (??)

Начнем с вопросительного знака (оператор нуля или один):

>>> import re
>>> re.findall('a?', 'aaaa')
['a', 'a', 'a', 'a', '']
>>> re.findall('a??', 'aaaa')
['', 'a', '', 'a', '', 'a', '', 'a', '']

В первую очередь вы используете нулевое или -ное Regex 'а? Отказ Это жадный, поэтому он соответствует одному «А» характер, если это возможно.

Во втором экземпляре вы используете не жадную версию NOL-OR-ONE «А ??» Отказ Это соответствует нулю «А» если возможно. Обратите внимание, что он движется слева направо Так что это соответствует пустой строке и «потребляет». Только тогда он больше не может сравниться с пустой строкой, так что она вынуждена соответствовать первым «А» персонаж. Но после этого он может снова соответствовать пустую строку. Этот шаблон первого сопоставления пустой строки и только тогда соответствует «А» Если это абсолютно необходимо, повторяется. Вот почему этот странный узор возникает.

Не жадный Asterisk Оператор (*?)

Начнем с звездочки (оператор нуля или больше):

>>> re.findall('a*', 'aaaa')
['aaaa', '']
>>> re.findall('a*?', 'aaaa')
['', 'a', '', 'a', '', 'a', '', 'a', '']

Во-первых, вы используете нулевое или больше Asterisk Regex 'A *' Отказ Это жадный, так что это соответствует столько же «А» символы, как это может.

Во-вторых, вы используете не жадную версию ноль или одну или одну или одну или одну или одну ‘A *? Отказ Опять же, это соответствует нулю «А» если возможно. Только если он уже соответствовал нулевым символам в определенной позиции, он соответствует одному символу в этом положении «потребляет» и перемещается.

Не жадный плюс оператор (+?)

Начнем с плюс (один или более оператор):

>>> re.findall('a+', 'aaaa')
['aaaa']
>>> re.findall('a+?', 'aaaa')
['a', 'a', 'a', 'a']

Во-первых, вы используете одно или-другое плюс Regex 'A +' Отказ Это жадный, так что это соответствует столько же «А» символы, как это может (но хотя бы один).

Во-вторых, вы используете не жадную одноразовую версию «А +?» Отказ В этом случае двигатель Regex соответствует только одному символу «А» Поглощает его и движется с следующим матчем.

Давайте суммируемся тем, что вы узнали до сих пор:

Жадные против не жадного матча – в чем разница?

Учитывая шаблон с квантификатором (например, оператор Asterisk), который позволяет двигатель REGEX соответствовать шаблону несколько раз.

Данная строка может сравниться с регулярным выражением несколькими способами. Например, обе подстроки «А» и «ААА» Допустимые совпадения при сопоставлении шаблона 'A *' В строке «АААА» Отказ

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

Примеры жадные против не жадного матча

Давайте рассмотрим ряд примеров, которые помогут вам понять разницу между жадными и не жадными спичками в Python:

>>> import re
>>> re.findall('a+', 'aaaa')
['aaaa']
>>> re.findall('a+?', 'aaaa')
['a', 'a', 'a', 'a']
>>> re.findall('a*', 'aaaa')
['aaaa', '']
>>> re.findall('a*?', 'aaaa')
['', 'a', '', 'a', '', 'a', '', 'a', '']
>>> re.findall('a?', 'aaaa')
['a', 'a', 'a', 'a', '']
>>> re.findall('a??', 'aaaa')
['', 'a', '', 'a', '', 'a', '', 'a', '']

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

Что быстрее: Жадные против не жадных?

Учитывая, что жадные квантования соответствуют максимальному и не жадным минимальным количеством шаблонов, есть ли разница в производительности?

Отличный вопрос!

Действительно, некоторые ориентиры предполагают, что существует значительная разница в производительности: жадный квантификатор на 100% медленнее в Реалистичные эксперименты по тестам данных Отказ

Поэтому, если вы оптимизируете для скорости, и вам не волнуйтесь о жадных или не жадных матчах – и вы ничего не знаете на не жадный квантификатор!

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

>>> import timeit
>>> timeit.timeit('import re;re.findall("a*", "aaaaaaaaaaaa")')
1.0579840000000331
>>> timeit.timeit('import re;re.findall("a*?", "aaaaaaaaaaaa")')
3.7830938000000742

Я использовал инструмент тестирования скорости Время течения Это позволяет бросить несколько простых утверждений Python и проверьте, как долго они бегают. По умолчанию пропущенное заявление выполнено в 1 000 000 раз.

Вы можете увидеть отличную разницу производительности более чем на 300%! Не жадная версия в три раза медленнее, чем жадная версия.

Это почему?

Причина – это метод Re.findall (), который возвращает список подходящих подстрок. Вот вывод обоих утверждений будет произведен:

>>> re.findall("a*", "aaaaaaaaaaaa")
['aaaaaaaaaaaa', '']
>>> re.findall("a*?", "aaaaaaaaaaaa")
['', 'a', '', 'a', '', 'a', '', 'a', '', 'a', '', 'a', '', 'a', '', 'a', '', 'a', '', 'a', '', 'a', '', 'a', '']

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

Итак, что произойдет, если вы используете метод Re.Search (), который возвращает только первый матч, а не метод Re.findall (), который возвращает все совпадения?

>>> timeit.timeit('import re;re.search("a*", "aaaaaaaaaaaa")')
0.8420328999998219
>>> timeit.timeit('import re;re.search("a*?", "aaaaaaaaaaaa")')
0.7955709000000297

Как и ожидалось, это снова меняет вещи. Оба поиска REGEOX дают один результат, но не жадный матч намного короче: он соответствует пустой строке '' а не всю строку 'AAAAAAAAAAAAAA' Отказ Конечно, это немного быстрее.

Однако разница незначительна в этом минимальном примере.

Есть больше: Жадный, послушный, ленивый, полезный, притяжательный матч

В этой статье я рассказал мир Regex в жадные и не жадные кванты. Но вы можете дифференцировать «не жадный» класс еще больше!

Далее я дам вам короткий обзор на основе Эта отличная статья из самых важных терминов в этом отношении:

  • Жадный : Сопоставьте как можно больше экземпляров количественного шаблона.
  • Послушные : Подберите столько экземпляров количественного шаблона до тех пор, пока он по-прежнему соответствует общему шаблону – если это возможно. Обратите внимание, что то, что я назвал «жадным» в этой статье, действительно «послушным».
  • Ленивый : соответствовать нескольким случаям количественного определенного рисунка по мере необходимости. Это то, что я назвал «не жадным» в этой статье.
  • Притяжательный : Никогда не отказывается от частичного матча. Таким образом, двигатель Regex может даже не найти совпадение, который на самом деле существует – только потому, что это так жадно. Это очень необычно, и вы не увидите его на практике.

Если вы хотите узнать больше о том, я бы порекомендовал, чтобы вы прочитали это отличное онлайн-учебное пособие Отказ

Куда пойти отсюда

Резюме: Вы узнали, что жадные кванты ? С * и + Сопоставьте как можно больше повторений количественного шаблона. Не жадные кванты ?? , *? и +? Сопоставьте как можно меньше повторений количественного шаблона.

Если вы хотите овладеть Python и регулярными выражениями, присоединяйтесь к моей бесплатной электронной почте Academy – от удовольствия!

Курс Python Regex

Инженеры Google являются регулярными мастерами. Система поисковой системы Google – это массивная Текстово-обработка двигателя Это извлекает значение из триллионов веб-страниц.

Инженеры Facebook являются регулярными мастерами экспрессии. Социальные сети, такие как Facebook, WhatsApp, и Instagram Подключите людей через Текстовые сообщения Отказ

Инженеры Amazon являются регулярными мастерами экспрессии. Ecommerce Giants корабля продуктов на основе Описания текстовых продуктов Отказ Регулярные выражения правит игре, когда текстовая обработка соответствует информатике.

Если вы хотите стать регулярным мастером выражения, проверьте Самый полный курс Python Regex на планете:

Работая в качестве исследователя в распределенных системах, доктор Кристиан Майер нашел свою любовь к учению студентов компьютерных наук.

Чтобы помочь студентам достичь более высоких уровней успеха Python, он основал сайт программирования образования Finxter.com Отказ Он автор популярной книги программирования Python одноклассники (Nostarch 2020), Coauthor of Кофе-брейк Python Серия самооставленных книг, энтузиаста компьютерных наук, Фрилансера и владелец одного из лучших 10 крупнейших Питон блоги по всему миру.

Его страсти пишут, чтение и кодирование. Но его величайшая страсть состоит в том, чтобы служить стремлению кодер через Finxter и помогать им повысить свои навыки. Вы можете присоединиться к его бесплатной академии электронной почты здесь.