Программирование и IT
Python: регулярные выражения
Регулярное выражение — шаблон, описывающий множество строк. В Python с ними работает модуль `re` из стандартной библиотеки. Шаблон пишут в строке с префиксом `r`, ищут через `re.search`, а результат достают из объекта совпадения. Всё остальное — детали языка шаблонов.
В этой статье
Когда шаблон уместен#
Регулярка выигрывает там, где структура текста нечёткая: выдернуть все даты из
абзаца, проверить формат кода, найти повторяющиеся пробелы. Если же строка
разделена запятыми или табуляцией, обычный split быстрее, читается лучше и
не ломается от лишней скобки — смотри разбор
строковых методов. Разбирать регуляркой HTML или JSON
не стоит вовсе: для них есть готовые библиотеки.
Первый поиск#
=
=
# 1207
# (6, 10)
# ['1207', '21', '09', '2026']
Префикс r перед строкой отключает обработку обратных слэшей самим Python:
без него "\d" пришлось бы писать как "\\d". Привыкай ставить r всегда.
Четыре функции покрывают почти всё:
re.search— первое совпадение в любом месте строки, иначеNone;re.match— совпадение только с начала строки;re.fullmatch— вся строка целиком;re.findall— список всех совпадений.
# None — строка начинается с буквы
# True
Путаница search и match — самая частая причина «шаблон правильный, а не
находит».
Из чего состоит шаблон#
| Запись | Значение |
|---|---|
. |
любой символ, кроме перевода строки |
\d \w \s |
цифра, буква или цифра или _, пробельный символ |
\D \W \S |
то же с отрицанием |
[абв] [а-я] |
любой символ из набора или диапазона |
[^абв] |
любой символ вне набора |
* + ? |
ноль и больше, один и больше, ноль или один |
{2} {2,5} |
ровно два, от двух до пяти |
^ $ |
начало и конец строки |
| |
или |
(...) |
группа, её содержимое достаётся отдельно |
В Python 3 шаблон по умолчанию работает с Unicode, поэтому \w находит и
кириллицу: re.findall(r"\w+", "мама мыла раму") вернёт все три слова.
Группы дают доступ к частям совпадения:
=
# 21.09.2026
# 21
# ('21', '09', '2026')
=
# 02
Жадность — главная ловушка#
Квантификаторы по умолчанию жадные: берут как можно больше, а потом отдают
назад ровно столько, сколько нужно для совпадения.
=
# ['<b>жирный</b> и <i>курсив</i>']
# ['<b>', '</b>', '<i>', '</i>']
# ['<b>', '</b>', '<i>', '</i>']
Первый шаблон захватил всю строку от первой < до последней >. Знак ?
после квантификатора делает его ленивым — берётся минимум. Третий вариант,
через отрицающий набор, работает быстрее ленивого и обычно предпочтительнее.
Вторая мина рядом: findall при наличии групп возвращает не сами совпадения,
а содержимое групп.
# [('1', '2'), ('3', '4')]
# ['2', '4']
Если группа нужна только для скобок, делай её незахватывающей — (?:...).
Замена, разбиение, флаги#
# много пробелов и переносов
# 2026-09-21
# ['а', 'б', 'в']
# ['раз', 'два']
# ['Кот', 'кот']
В замене \1, \2 ссылаются на группы — строку замены тоже пишут с r.
Флаг re.MULTILINE заставляет ^ и $ срабатывать на каждой строке,
re.IGNORECASE отключает регистр, re.DOTALL разрешает точке совпадать с
переводом строки.
Если один шаблон применяется в цикле, скомпилируй его один раз:
pattern = re.compile(r"\d+"), дальше pattern.findall(text).
Частые ошибки#
Обращение к None. re.search(...).group() падает с
AttributeError: 'NoneType' object has no attribute 'group', когда совпадения
нет. Всегда проверяй: m = re.search(...), затем if m:. Подробнее об
аккуратной обработке — в статье про
исключения.
Забытый r. Без него "\b" — это символ забоя, а не граница слова, и
шаблон молча перестаёт находить.
Неэкранированная точка. \d+.\d+ совпадёт с 12x34, потому что точка —
любой символ. Для буквальной точки пишут \., а для экранирования целой
подстроки есть re.escape.
Практика: напиши шаблон, который достаёт из текста все адреса вида
слово@слово.домен, и второй — который заменяет два и больше пробелов подряд
одним. Проверь оба на строке, где заведомо есть и подходящие, и похожие, но
неподходящие куски.
План по этапам
- Поиск цифрНайти в строке первое число через re.search и все числа через re.findall.
- Сравнить search и matchПрименить обе функции к строке, где совпадение не в начале, и объяснить None.
- Достать группыРазобрать дату шаблоном с тремя группами и вывести день, месяц, год по отдельности.
- Жадность против лениПрогнать <.*> и <.*?> на одной строке с тегами и записать разницу.
- Замена с обратными ссылкамиПереставить части даты через re.sub с \1 и \3.
Начать изучать эту тему у себя
План ляжет в твой репозиторий: отмечай этапы, веди конспект — история изменений покажет, как ты продвинулся.
Проверь себя
1.Сколько элементов вернёт re.findall(r"\d+", "заказ 1207 от 21.09.2026")?
2.Что вернёт re.match(r"\d+", "заказ 1207")?
3.Что найдёт re.findall(r"<.*>", "<b>x</b>") — сколько элементов в списке?
4.Какой квантификатор делает поиск ленивым — минимально возможным?
Источники
-
Модуль re в документации PythonПолный синтаксис шаблонов, функции и флагибесплатно
-
Regular Expression HOWTOОфициальное пошаговое введение с примерамибесплатно
-
StepikКурсы с задачами на регулярные выражениябесплатно
Было полезно?