python
September 19, 2021

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

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

Отмена вывода группы

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

import re
s1 = 'г.Москва, ул. Коштоянца д. 5'
s2 = '-Москва, проспект Чайковского 3, д.2'
s3 = '<span>текст</span>'
re.search(r'(г\.)([А-Яа-яеЁ]+)', s1).groups()

Как мы видим, все, что заключено в круглые скобки включается в итоговый вывод. Но так как эти скобки также используются для группировки выражений, такой поведение не всегда желательно. Если мы хотим отменить вывод следует после открывающейся скобки добавить два следующих символа ":?":

re.search(r'(?:г\.)([А-Яа-яеЁ]+)', s1).groups()

Условный поиск

В регулярных выражениях можно задавать разные конструкции поиска в зависимости от нахождения шаблона. В общем виде это выглядит так:

(?(id или name шаблона)вариант1|вариант2)

Вот пример работы для наших двух строк в зависимости от наличия в них символов "г.":

re.search(r'(г\.)?(?(1)(М)|(-М))', s2).groups()
re.search(r'(г\.)?(?(1)(М)|(-М))', s1).groups()

Обратите внимание, если убрать в строке s2 символ "-", то получим ошибку, так как регулярное выражение требует, чтобы при условии отсутствия "г." в строке имелось "":

Именование частей

Исключительную пользу при работе со сложными регулярными выражениями представляет именование групп поиска, которое задается после открывающейся скобки символами "?P<имя>". Впоследствии именованные группы можно получить в виде словаря посредством метода groupdict. Перепишем способ поиска по условной конструкции с включением именования:

# naming
re.search(r'(?P<g>г\.)?(?(g)(?P<let1>М)|(?P<let2>-М))', s1).groupdict()
re.search(r'(?P<g>г\.)?(?(g)(?P<let1>М)|(?P<let2>-М))', s2).groupdict()

Ссылки на поисковые вхождения

На группы вхождения можно ссылаться в последующих частях регулярного выражения, что полезно, например, при поиске в тегах. Для ссылки по id группы используется конструкция "\id", а по имени (?P=name):

re.search(r'<([\w]+)>([А-Яа-яёЁ]+)</\1>', s3).groups()
re.search(r'<(?P<tag>[\w]+)>([А-Яа-яёЁ]+)</(?P=tag)>', s3).groups()