Python · СправочникРегулярные выражения и модуль re
SilverTests.ruregex · re · pattern · match
Регулярные выражения
Одна строка-шаблон находит, проверяет и преобразует текст — без циклов и условий
1Зачем нужны регулярные выражения
Представь, что ты ведёшь паблик класса во ВКонтакте, и за день там 500 сообщений. Нужно посчитать, сколько раз упоминается новая учительница — но ребята пишут по-разному: @anna_p, А. П., Анна Петровна, АП. Или другая задача: выдрать из объявлений все телефоны, все email, все даты. Или проверить, что пароль на сайте школьного кружка достаточно надёжный.
Два плохих решения
Искать подстрокой через in. Работает, только если ты точно знаешь, что ищешь: "Анна" in message. Но ты не знаешь точно — формат меняется. Придётся писать десять проверок, и всё равно что-то пропустишь.
Разбирать строку посимвольно в цикле. Принципиально можно. Практически — 40 строк кода на одну задачу, куча if-ов, и при малейшем изменении формата всё ломается. Программа превращается в лабиринт.
Хорошее решение — регулярное выражение
Регулярное выражение (regex, регулярка) — это короткая строка-шаблон, которая описывает форму искомого текста. Мы описываем не конкретные буквы, а устройство: «знак плюс, цифра 7, потом три цифры, потом ещё семь цифр с любыми разделителями». Модуль re по шаблону ищет, проверяет или заменяет — за одну строку кода.
| Что умеет regex |
Функция |
Пример задачи |
| найти первое совпадение |
re.search |
проверить, есть ли в сообщении номер телефона |
| найти все совпадения |
re.findall |
собрать все хештеги из поста |
| проверить формат целиком |
re.fullmatch |
валидировать пароль при регистрации |
| заменить все совпадения |
re.sub |
замаскировать номера телефонов звёздочками |
| разбить текст по шаблону |
re.split |
разделить строку и по пробелам, и по запятым |
Чего regex НЕ делает. Не понимает смысл текста, не знает, что «кот» — животное. Не умеет считать вложенность (например, парные скобки с произвольной глубиной — это не регулярный язык). Не заменяет парсер для сложных форматов — для JSON, XML и HTML лучше брать специальные библиотеки.
2Импорт модуля и raw-строки
Модуль re встроен в Python — ставить ничего не нужно:
import re
text = "Сегодня 17 апреля, завтра 18 апреля"
result = re.findall(r"\d+", text)
print(result) # ['17', '18']
Обрати внимание на r перед кавычками — это raw-строка. В ней обратный слэш \ остаётся обратным слэшем и не перехватывается Python. А в регулярках \ встречается постоянно (\d, \w, \b...), поэтому raw-строки — стандарт.
Ловушка. Без r запись "\b" Python превратит в символ backspace (\x08) — и регулярка сломается ещё до того, как дойдёт до re. Пиши r"\b" — получишь именно границу слова. Если твой шаблон вдруг «не находит то, что очевидно должен» — первым делом проверь префикс r.
3Из чего состоит шаблон
Большинство символов в шаблоне означают сами себя: r"кот" найдёт подстроку «кот». Но есть метасимволы — знаки с особым смыслом. Их всего 12:
. ^ $ * + ? { } [ ] \ | ( )
Чтобы найти такой символ буквально, его экранируют обратным слэшем: r"\." ищет точку, r"\?" ищет знак вопроса.
Точка и классы символов
Точка . — это «любой символ, кроме перевода строки». А если нужно разрешить конкретный набор — используется класс в квадратных скобках: [аоуыэ] — одна из пяти гласных; [0-9] — любая цифра; [а-я] — любая строчная кириллическая буква. С ^ в начале класс инвертируется: [^0-9] — любой символ кроме цифры.
Есть сокращения для популярных классов:
| Шаблон |
Что совпадает |
Эквивалент |
| \d |
одна цифра |
[0-9] |
| \D |
не цифра |
[^0-9] |
| \w |
буква, цифра или _ |
[A-Za-zА-Яа-я0-9_] |
| \W |
не \w |
[^\w] |
| \s |
пробел, таб, перевод строки |
[ \t\n\r\f\v] |
| \S |
не \s |
[^\s] |
Попробуй — нажми на класс, чтобы подсветить в тексте все его символы
Нажми на шаблон класса — подсветятся все подходящие символы в образце.
В Python 3 \w по умолчанию захватывает и латиницу, и кириллицу — это удобно. Если нужно ограничиться только ASCII, есть флаг re.ASCII.
4Квантификаторы: сколько раз повторить
После любого элемента шаблона можно поставить квантификатор — указать, сколько раз элемент повторяется.
| Запись |
Значение |
| * |
0 или больше раз |
| + |
1 или больше раз |
| ? |
0 или 1 раз (необязательный элемент) |
| {n} |
ровно n раз |
| {n,} |
n или больше |
| {n,m} |
от n до m раз включительно |
re.findall(r"\d+", "В 9А 28 учеников, в 9Б — 26")
# ['9', '28', '9', '26']
re.findall(r"\d{4}", "Родились в 2008, 2009 и 2010 годах")
# ['2008', '2009', '2010']
Жадность и ленивость
Квантификаторы по умолчанию жадные: хватают как можно больше. Это главный источник сюрпризов у новичков. Добавь ? после квантификатора — получится ленивый вариант: хватает минимально возможное.
Сравни — один и тот же текст, разные квантификаторы
Разница драматическая: жадный шаблон съедает всё от первого < до последнего > и возвращает одно гигантское совпадение; ленивый ищет минимальную подстроку между < и > и находит каждый тег отдельно.
5Якоря и границы слова
Якоря — это «невидимые» позиции в строке. Они ничего не съедают из текста, а проверяют, где мы находимся.
| Якорь |
Что значит |
| ^ |
начало строки |
| $ |
конец строки |
| \b |
граница слова (переход между \w и \W) |
| \B |
НЕ граница слова |
re.findall(r"\bкот\b", "кот и котёл, котик сказал мяу")
# ['кот'] — «котёл» и «котик» отброшены: граница не там
Про ^ и $. По умолчанию они означают начало и конец всего текста, а не отдельной строки. Чтобы они срабатывали на каждой строке, нужен флаг re.MULTILINE (о флагах ниже).
6Группы и альтернативы
Круглые скобки (...) создают группу: содержимое группы можно потом достать отдельно. Вертикальная черта | — это «или».
m = re.search(r"(\d{2})\.(\d{2})\.(\d{4})", "Дата: 07.03.2009")
m.group(0) # '07.03.2009' — вся находка
m.group(1) # '07' — первая группа
m.group(2) # '03'
m.group(3) # '2009'
re.findall(r"кот|пёс|попугай", "у меня кот, у друга попугай")
# ['кот', 'попугай']
Именованные группы
Номера группы легко перепутать, если их много. Удобнее давать имена — через (?P<имя>...):
m = re.search(r"(?P<day>\d{2})\.(?P<month>\d{2})\.(?P<year>\d{4})",
"07.03.2009")
m.group("year") # '2009'
m.groupdict() # {'day': '07', 'month': '03', 'year': '2009'}
Группа без захвата
Иногда скобки нужны только для того, чтобы применить к куску шаблона квантификатор — но занимать номер группы не хочется. Тогда ставь (?:...):
re.findall(r"(?:ха){2,}", "хахаха! ха ну ты даёшь")
# ['хахаха'] — только повторы от 2 раз
Обратные ссылки
Номер группы можно использовать внутри того же шаблона — через \1, \2... Это «сошлись на то, что совпало в этой группе». Мощная штука для поиска повторов:
re.findall(r"\b(\w+)\s+\1\b", "он был был очень очень рад")
# ['был', 'очень'] — нашли тавтологии
7Функции модуля re
Вот весь набор, который тебе реально пригодится:
| Функция |
Что делает |
Что возвращает |
| re.search(p, s) |
найти первое совпадение где угодно |
Match или None |
| re.match(p, s) |
совпадение с начала строки |
Match или None |
| re.fullmatch(p, s) |
вся строка должна совпасть целиком |
Match или None |
| re.findall(p, s) |
все совпадения |
список строк (или кортежей, если есть группы) |
| re.finditer(p, s) |
все совпадения с позициями |
итератор Match-объектов |
| re.sub(p, repl, s) |
заменить все совпадения |
новая строка |
| re.split(p, s) |
разбить по шаблону |
список кусков |
| re.compile(p) |
скомпилировать шаблон |
объект Pattern с теми же методами |
Если одна и та же регулярка используется много раз — её стоит скомпилировать заранее через re.compile. Так быстрее.
phone_rx = re.compile(r"\+7 \d{3} \d{3}-\d{2}-\d{2}")
for contact in contacts:
if phone_rx.fullmatch(contact):
...
Объект Match
Когда search, match или fullmatch находят — они возвращают объект Match:
| Метод |
Что возвращает |
| .group() |
всю найденную подстроку |
| .group(n) |
n-ю группу (нумерация с 1) |
| .groups() |
кортеж всех групп |
| .groupdict() |
словарь именованных групп |
| .start(), .end() |
индексы в исходной строке |
| .span() |
(start, end) |
Важно. Если совпадения нет, функция вернёт None. Вызов .group() у None даст AttributeError. Всегда проверяй результат через if m: ....
8Лаборатория — живой regex-стенд
Ничто не заменит эксперимента. Ниже — песочница: пиши шаблон, пиши текст, сразу видишь, что найдено. Переключай флаги. Для быстрого старта — пресеты с готовыми примерами.
Regex Lab — тестирование шаблона вживую
текст
В песочнице используется regex-движок JavaScript — он почти совпадает с Python-ным. Именованные группы (?P<name>...) автоматически переводятся в JS-синтаксис (?<name>...), поэтому можно писать по-питоновски.
9Флаги
Флаги меняют поведение всей регулярки. Передаются последним аргументом или через re.compile(..., flags=...).
| Флаг |
Кратко |
Что делает |
| re.I |
IGNORECASE |
регистр не важен: А = а, A = a |
| re.M |
MULTILINE |
^ и $ работают на каждой строке |
| re.S |
DOTALL |
. захватывает и перевод строки |
| re.X |
VERBOSE |
можно писать шаблон с пробелами и комментариями |
| re.A |
ASCII |
\w, \d — только латиница и цифры |
re.findall(r"python", "Python и PYTHON", re.I)
# ['Python', 'PYTHON']
# Несколько флагов объединяются через |
re.findall(r"^\w+", text, re.M | re.I)
Флаг re.X особенно полезен для длинных регулярок — разрешает переносы строк, отступы и комментарии прямо внутри шаблона:
phone = re.compile(r"""
(?:\+7|8) # код страны
[\s\-(]*
(\d{3}) # код оператора
[\s\-)]*
(\d{3}) # первая тройка
[\s\-]*
(\d{2}) # вторая пара
[\s\-]*
(\d{2}) # третья пара
""", re.VERBOSE)
10Замены через re.sub
В строке замены можно ссылаться на группы: \1, \2... или по имени через \g<name>.
re.sub(r"(\d{2})\.(\d{2})\.(\d{4})",
r"\3-\2-\1",
"Экзамен 27.05.2026")
# 'Экзамен 2026-05-27'
Вместо строки замены можно передать функцию. Она получит объект Match и должна вернуть строку-замену. Это мощный приём — можно реализовать какую угодно логику:
def inc(m):
return str(int(m.group()) + 1)
re.sub(r"\d+", inc, "купил 2 ручки и 5 тетрадей")
# 'купил 3 ручки и 6 тетрадей'
11Подводные камни
Не экранировал точку. r"www.site.ru" совпадёт и с «wwwXsiteYru». Правильно: r"www\.site\.ru".
Жадность сломала HTML. r"<.+>" съест всё от первого < до последнего >. Нужен r"<.+?>".
$ сработал не на каждой строке. По умолчанию $ — конец всего текста, а не строки. Нужен re.MULTILINE.
findall и группы. Если в шаблоне есть группы, findall возвращает содержимое групп, а не всё совпадение целиком. Когда нужны обе вещи — используй finditer.
Валидация email регуляркой — миф. Идеально корректного regex для email не существует. Для школьной задачи упрощённого варианта достаточно, в продакшене просто шлют письмо-подтверждение.
12Что запомнить
Главное о регулярках. Regex — это строка-шаблон, описывающая форму текста. Метасимволы (. ^ $ * + ? { } [ ] \ | ( )) имеют особый смысл; остальные — сами себя. Пиши шаблоны в raw-строках: r"...". Классы (\d, \w, [а-я]) описывают «один символ из множества», квантификаторы (*, +, {n,m}) — «сколько раз». Квантификаторы жадные по умолчанию — добавь ? для ленивости. Скобки создают группы: их содержимое можно потом достать через .group(n). Основные функции — search, findall, fullmatch, sub. Если совпадения нет, возвращается None — проверяй. Регулярка не заменяет парсер для JSON, XML, HTML — но для «найти все телефоны в тексте» это самый короткий путь из существующих.