Перейти к содержанию

192 уроков, 14 библиотек и челлендж «Что выведет код?» — бесплатно, код прямо в браузере

Начать обучение
Урок 1 из 12 Начальный 35 мин 100 XP

Регулярные выражения Python с нуля: первый шаблон и модуль re

Первый шаблон, который находит любую дату в тексте, а не одну конкретную: import re, re.search, raw-строки и объект Match — всё, чтобы перестать бояться регулярки.

Редакция Питоники

Перед вами типичная задача: в ворохе текста нужно найти дату. Не конкретную «14.03.2025», а любую: завтра в файле окажется «02.11.2024», послезавтра — «07.09.2025». Двадцать проверок if уже написано, а даты всё не находятся. Для таких задач в Python есть отдельный язык внутри языка — регулярные выражения и модуль re из стандартной библиотеки.

Регулярка описывает форму текста, а не конкретные слова: «две цифры, любой символ, две цифры, любой символ, четыре цифры» — это шаблон даты, и он найдёт и мартовскую, и ноябрьскую. В этом уроке вы запустите первый шаблон, узнаете, зачем шаблоны оборачивают в raw-строки r"", и научитесь читать email-шаблон по кусочкам. Весь код исполняется прямо на странице: модуль re работает в браузерной песочнице по-настоящему, без установки Python.

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

Регулярное выражение (regular expression, на жаргоне — regex, regexp или просто регулярка) — это строка-образец, которая описывает целый класс текстов. Обычная подстрока «кот» совпадает только с котом. Шаблон «к.т» совпадает уже с котом, китом и даже «кыт» — точка в середине означает «здесь любой символ».

Идея та же, что в файловых масках: *.jpg означает «любое имя, но обязательно jpg». Только словарь у регулярок богаче: есть готовые обозначения для «цифра», «буква или цифра», «пробел», «один или много раз». Из этих кирпичиков собираются шаблоны дат, цен, телефонов и адресов почты.

  • валидация форм: email, телефон и индекс проверяются одним шаблоном вместо кучи if;
  • парсинг: из HTML, логов и отчётов достают данные без ручного разбора строк;
  • чистка данных: заменить все даты и телефоны в выгрузке одной командой;
  • анализ текста: посчитать слово во всех его формах сразу;
  • Pandas и BeautifulSoup принимают регулярки в своих методах — навык переносится в соседние библиотеки.

Чем регулярки отличаются от строковых методов in и find?

У строк в Python уже есть инструменты поиска: оператор in, методы find и index. Почему бы не ими? Они ищут только точную подстроку. «Найди 14.03.2025» — пожалуйста. «Найди любую дату» — уже нет: метода, который понимает «две цифры», у строк не существует.

строковые методы ищут только точную подстроку
text = "Заказ отправлен 14.03.2025, ждите письмо."

# строковые методы видят только буквы и подстроки
print("14.03.2025" in text)        # нашли конкретную дату
print(text.find("2025"))           # и её позицию

# а теперь дата другая - метод бессилен
text2 = "Заказ отправлен 02.11.2024, ждите письмо."
print("14.03.2025" in text2)       # точного вхождения нет
Вывод
True
22
False

in и find выросли из вопроса «есть ли эта конкретная подстрока?». Когда условие превращается в «какие-то символы, потом цифры, потом ещё что-то», методы заканчиваются — и начинается цикл с проверкой каждого символа вручную. Регулярка закрывает именно этот случай: структура текста описывается одной строкой шаблона, а не десятью if.

За регулярки в Python отвечает модуль re. Он входит в стандартную библиотеку, поэтому ничего устанавливать не нужно — достаточно import re. Главная функция поиска — re.search(pattern, text): она ищет шаблон pattern в строке text и возвращает первое совпадение.

первый шаблон: ищем дату
import re

text = "Заказ отправлен 14.03.2025, ждите письмо."
m = re.search(r"\d\d.\d\d.\d\d\d\d", text)
print(m)
print(m.group())
Вывод
<re.Match object; span=(16, 26), match='14.03.2025'>
14.03.2025

Разберём строку с шаблоном. r"" в начале — raw-строка, про неё через минуту. Внутри: \d — одна цифра, значит \d\d — две подряд; точка между группами — «любой символ» (её роль сыграли точки в дате); \d\d\d\d — четыре цифры года. Первый шаблон читается так: «две цифры, любой символ, две цифры, любой символ, четыре цифры».

re.search прошла по строке слева направо и на позиции 16 нашла кусок, подходящий под описание: «14.03.2025». Результат — не строка, а специальный объект Match: в нём и что нашли, и где именно. Метод .group() вытаскивает сам найденный текст.

Зачем шаблонам raw-строки r""?

Python и регулярки имеют собственные представления об обратном слэше. Для Python «\n» — перевод строки, «\t» — табуляция. Для регулярки слэш — сигнал «дальше спецсимвол»: \d — цифра. Когда шаблон пишут в обычной строке, два словаря начинают спорить за одни и те же символы.

обычная строка и raw-строка - одно и то же
import re

# без r"" обратный слэш приходится удваивать
pattern_plain = "\\d\\d.\\d\\d.\\d\\d\\d\\d"
pattern_raw = r"\d\d.\d\d.\d\d\d\d"

print(pattern_plain == pattern_raw)   # это одна и та же строка
print(re.search(pattern_raw, "встреча 07.09.2025 в 15:00").group())
Вывод
True
07.09.2025

В обычной строке каждый служебный слэш пришлось удвоить — представьте телефонный шаблон в таком виде: глазами не прочитать. Raw-строка r"..." говорит Python: «слэши не трогай, передавай как есть». Содержимое у обеих строк одинаковое, это проверил блок выше, но запись с r короче и честнее.

как Python искажает слэши в обычной строке
s1 = "раз\nдва"      # \n Python заменяет на перевод строки
s2 = r"раз\nдва"     # raw-строка: два отдельных символа

print(len(s1))       # перевод строки считается одним символом
print(len(s2))       # здесь \n - слэш и буква n
print(repr(s2))
Вывод
7
8
'раз\\nдва'

В raw-версии ровно то, что написано: слэш и буква n как два отдельных символа. repr даже показывает слэш удвоенным, чтобы его нельзя было спутать с настоящим переводом строки. Шаблоны в raw-строках не искажаются, поэтому правило простое: любой шаблон — в r"". Единственное исключение — шаблон, заканчивающийся на нечётное число слэшей: raw-строку с ним записать нельзя, придётся удвоить слэш вручную.

Что возвращает re.search: объект Match?

При удаче re.search возвращает объект Match — маленькую папку с данными о совпадении. Самое нужное в ней: .group() — найденный текст, .start() и .end() — границы в исходной строке, .span() — обе границы сразу, кортежем.

атрибуты объекта Match
import re

text = "Цена: 199 рублей"
m = re.search(r"\d+", text)

print(m.group())    # что нашли
print(m.start())    # где начинается
print(m.end())      # где заканчивается
print(m.span())     # обе границы разом
Вывод
199
6
9
(6, 9)

Границы отсчитываются в символах исходной строки с нуля: в «Цена: 199 рублей» совпадение «199» занимает символы с 6-го по 9-й, не включая 9-й. Это удобно, когда найденное нужно не только показать, но и вырезать или заменить по месту. Заметьте: в шаблоне впервые мелькнул плюс — «один или больше раз», подробно о нём в уроке про квантификаторы.

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

разрезаем строку по границам совпадения
import re

text = "Приходите 21.09.2026 на открытие"
m = re.search(r"\d\d.\d\d.\d\d\d\d", text)

print(text[:m.start()])         # до даты
print(text[m.start():m.end()])  # сама дата
print(text[m.end():])           # после даты
Вывод
Приходите 
21.09.2026
 на открытие

Дата вырезана, а по краям остались хвосты — пробелы в них видны, если присмотреться к выводу. В реальном коде хвосты подчищают методом strip() или сразу строят шаблон так, чтобы он захватывал меньше лишнего.

А если совпадения нет? re.search возвращает None — это не ошибка, а нормальный ответ «ничего не нашёл».

нет совпадения - получаем None
import re

m = re.search(r"\d\d\d", "тут нет чисел")
print(m)

if m is None:
    print("Совпадений нет - вызывать m.group() нельзя")
Вывод
None
Совпадений нет - вызывать m.group() нельзя

Как выглядит первая настоящая программа с регуляркой?

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

поиск даты в каждой заметке
import re

notes = [
    "встреча 07.09.2025 в 15:00",
    "чек от 12/05/2024 не нашли",
    "даты нет, только текст",
]

for note in notes:
    m = re.search(r"\d\d.\d\d.\d\d\d\d", note)
    if m:
        print("Дата:", m.group())
    else:
        print("Дата не найдена")
Вывод
Дата: 07.09.2025
Дата: 12/05/2024
Дата не найдена

Обратите внимание на вторую заметку: шаблон поймал «12/05/2024», хотя даты обычно пишут через точку. Виновата точка в шаблоне — она означает «любой символ» и с радостью сыграла роль слэша. Иногда это удобно: один шаблон ловит несколько форматов записи. Но чаще нужна точность, и как искать именно точку — тема следующего урока о классах символов.

Как прочитать страшный шаблон по кусочкам: пример с email?

Пора снять страх перед «настоящими» регулярками. Вот шаблон email из боевого кода — не идеальный, но рабочий и встречается везде:

типовой email-шаблон - как есть
r"^\w+@\w+\.\w+$"

# как его читают:
# ^      - начало строки
# \w+    - буквы, цифры или подчёркивания, один или больше раз
# @      - обычная собака, никакой магии
# \w+    - имя домена до точки
# \.     - настоящая точка (экранированная)
# \w+    - домен верхнего уровня: ru, com
# $      - конец строки
Этот шаблон можно запустить — он показан как есть, без обвязки: оберните его в re.search и проверьте на своём адресе. Буква r перед строкой обязательна: в шаблоне четыре обратных слэша.

Семь кусочков — семь простых слов, и шаблон прочитан. Новых значков здесь два: ^ и $ закрепляют шаблон за началом и концом строки, а плюс означает «один или больше раз»; его подробный разбор — в третьем уроке, все способы поиска собраны в уроке про методы re. Заметьте: точка перед доменом экранирована слэшем. Без экранирования она означала бы «любой символ» и пропускала бы адреса вида user@site-ru, где вместо точки стоит дефис.

Что дальше

Вы сделали главное: запустили первый шаблон, разобрались с raw-строками и объектом Match, прочитали по кусочкам email-шаблон. Дальше тема раскрывается слоями: второй урок — классы символов \d, \w, \s и собственные наборы в квадратных скобках; третий — квантификаторы, которые превращают «две цифры» в «сколько угодно цифр» без копипасты. А когда захочется вытащить из страницы все ссылки, пригодится урок про извлечение данных в BeautifulSoup — там регулярки работают в паре с парсером.

Регулярка описывает форму текста, а не конкретные слова: две цифры, любой символ, две цифры, любой символ, четыре цифры.

Что выведет код?

Сначала предскажи ответ в голове — это главный навык программиста.

import re

m = re.search(r"\d+", "Заказ 42 готов")
print(m.group(), m.start())
import re

text = "встреча в 09:15"
print(re.search(r"\d\d.\d\d", text).group())
import re

print(re.search(r"\d\d\d", "код 25"))
Проверь себя
0 / 5

1. Что вернёт re.search, если совпадений в тексте нет?

2. Какой символ в шаблоне означает «любой символ, кроме перевода строки»?

3. Зачем шаблон записывают в raw-строке r""?

4. Что вернёт m.span() для совпадения «199» в строке «Цена: 199 рублей»?

5. Что напечатает print(re.search(r"\d\d", "код 7б42").group())?

Карточки терминов
Запомнено: 0 / 6
Практика

В редакторе — строка с датой посылки. Найдите дату шаблоном «две цифры, любой символ, две цифры, любой символ, четыре цифры» и выведите строку «Дата: ...», где вместо многоточия — найденный текст через m.group().

practice.py
Вопросы и ответы по уроку

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

Это язык описания образцов текста: шаблон «две цифры, любой символ, две цифры» находит любую дату, а не одну конкретную. Вместо циклов и кучи if пишется одна строка с шаблоном, а поиск выполняет модуль re — стандартная часть Python.

Как подключить регулярные выражения в Python?

Никакой установки не нужно: модуль re входит в стандартную библиотеку. Достаточно написать import re в начале файла и вызвать re.search(шаблон, строка). В браузерной песочнице этой страницы модуль уже доступен — код запускается прямо в уроке. Для свободных экспериментов со своими шаблонами есть ](/playground/) — тот же интерпретатор, без привязки к уроку.

Зачем перед шаблоном ставят букву r?

r"" делает строку raw: Python передаёт обратные слэши регулярке как есть, не превращая \n в перевод строки. Без raw-строки шаблон может тихо изменить смысл — и вместо цифры \d регулярка начнёт искать перенос строки. Пишите r перед каждым шаблоном.

Что возвращает re.search, если ничего не нашла?

None. Это не ошибка, а честный ответ «совпадений нет», но вызывать .group() у None нельзя — упадёт с AttributeError. Проверяйте результат условием if m: перед тем, как доставать текст.

Понравился урок? Сошлитесь на него

«Регулярка описывает форму текста, а не конкретные слова: две цифры, любой символ, две цифры, любой символ, четыре цифры.»

Скопируйте готовую ссылку в формате HTML, Markdown или чистый адрес и вставьте в статью на Habr, VC, Telegram-канал или свой блог — так о проекте узнают новые читатели.

TelegramVK

Похожие уроки по темам

Подобраны автоматически по пересечению тем и ключевых слов.