Модуль re — регулярные выражения в Python

Регулярные выражения (regex, regexp) — это маленький язык для описания шаблонов в тексте. С их помощью можно найти e-mail в логе, выдернуть номер телефона из строки, проверить пароль на правила, разобрать формат файла. Это один из самых универсальных инструментов в любом современном языке программирования.

В Python для работы с regex есть стандартный модуль re:

import re

Первый пример

import re

text = 'Сегодня 14 мая 2026 года, температура 23 градуса.'
numbers = re.findall(r'\d+', text)
print(numbers)
# ['14', '2026', '23']

\d+ — это шаблон: «одна или больше цифр подряд». Модуль прошёл по строке и вернул все совпадения.

Совет

Регулярки пишут с префиксом r'' (raw-string). Иначе обратные слэши \d, \w, \s придётся удваивать.


Основные конструкции

.       любой символ кроме переноса строки
\d      цифра 0-9            \D   не цифра
\w      буква/цифра/_         \W   не буква/цифра
\s      пробел/таб/перенос    \S   не пробел
^       начало строки
$       конец строки
*       0 и более повторений предыдущего
+       1 и более повторений
?       0 или 1 (опционально)
{n}     ровно n повторений
{n,m}   от n до m повторений
[abc]   один из перечисленных символов
[a-z]   диапазон
[^abc]  любой кроме перечисленных
|       или (apple|banana)
()      группа
\b      граница слова

Основные функции модуля re

  • re.match(pat, s) — проверяет совпадение в начале строки.

  • re.search(pat, s) — ищет первое совпадение в любом месте.

  • re.findall(pat, s) — возвращает список всех совпадений.

  • re.finditer(pat, s) — то же, но итератором (с объектами Match).

  • re.sub(pat, repl, s) — заменяет совпадения.

  • re.split(pat, s) — режет строку по шаблону.

  • re.fullmatch(pat, s) — проверяет, что вся строка соответствует шаблону.


Пример: проверка e-mail

import re

pattern = r'^[\w.+-]+@[\w-]+\.[\w.-]+$'

def is_email(s):
    return re.match(pattern, s) is not None

print(is_email('user@example.com'))     # True
print(is_email('user@@example.com'))    # False
print(is_email('просто текст'))         # False

Это упрощённый шаблон — настоящая RFC-валидная регулярка для e-mail монструозна. Для большинства задач хватает простой проверки.


Группы и захват

Скобки () создают группу — кусок, который можно «вынуть» отдельно.

import re

text = 'Сегодня 14.05.2026'
m = re.search(r'(\d{2})\.(\d{2})\.(\d{4})', text)
if m:
    day, month, year = m.groups()
    print(year, month, day)
    # 2026 05 14

Именованные группы делают код понятнее:

m = re.search(r'(?P<day>\d{2})\.(?P<month>\d{2})\.(?P<year>\d{4})', text)
print(m['year'])    # 2026
print(m['day'])     # 14

Замена с группами: re.sub

import re

text = 'Дата: 14.05.2026'
result = re.sub(r'(\d{2})\.(\d{2})\.(\d{4})', r'\3-\2-\1', text)
print(result)
# Дата: 2026-05-14

В строке замены \1, \2, \3 ссылаются на захваченные группы.


Флаги

  • re.IGNORECASE (re.I) — игнорировать регистр.

  • re.MULTILINE (re.M) — ^ и $ совпадают с началом/концом каждой строки, а не всего текста.

  • re.DOTALL (re.S) — . начинает совпадать и с переносом строки.

  • re.VERBOSE (re.X) — разрешает пробелы и комментарии внутри шаблона.

import re

text = 'Python и PYTHON и pYtHoN'
print(re.findall(r'python', text, re.IGNORECASE))
# ['Python', 'PYTHON', 'pYtHoN']

Флаг VERBOSE для длинных шаблонов:

phone_pattern = re.compile(r'''
    \+?           # необязательный плюс
    (\d{1,3})     # код страны
    [\s\-]?       # разделитель
    \(?(\d{3})\)? # код города
    [\s\-]?
    (\d{3})       # 3 цифры
    [\s\-]?
    (\d{2})       # 2 цифры
    [\s\-]?
    (\d{2})       # 2 цифры
''', re.VERBOSE)

Компиляция шаблона

Если один и тот же шаблон используется много раз, скомпилируйте его заранее — это быстрее:

import re

PHONE = re.compile(r'\+?\d{1,3}\s?\(?\d{3}\)?\s?\d{3}-?\d{2}-?\d{2}')

for line in lines:
    if PHONE.search(line):
        print('Нашёл телефон:', line)

Практический пример: парсинг логов

Допустим, у нас типичный лог:

2026-05-14 10:23:45 [INFO] User 42 logged in
2026-05-14 10:24:01 [ERROR] Connection refused
2026-05-14 10:24:18 [INFO] User 7 logged out

Распарсим в словари:

import re

LOG = re.compile(r'''
    (?P<date>\d{4}-\d{2}-\d{2})\s+
    (?P<time>\d{2}:\d{2}:\d{2})\s+
    \[(?P<level>\w+)\]\s+
    (?P<message>.+)
''', re.VERBOSE)

with open('app.log', encoding='utf-8') as f:
    for line in f:
        m = LOG.match(line)
        if m:
            print(m.groupdict())

Жадные и ленивые квантификаторы

По умолчанию +, * и {n,} жадные — стараются захватить как можно больше. Добавьте ? после квантификатора, чтобы сделать его ленивым:

import re

html = '<b>привет</b> <i>мир</i>'
print(re.findall(r'<.+>', html))    # ['<b>привет</b> <i>мир</i>'] — жадно
print(re.findall(r'<.+?>', html))   # ['<b>', '</b>', '<i>', '</i>'] — лениво

Подводные камни

Предупреждение

  • ``re.match`` не ищет в середине! Он проверяет только начало. Чтобы искать везде — re.search.

  • HTML/XML регулярками не парсят. Используйте html.parser, BeautifulSoup или lxml. Regex не понимает вложенность.

  • Жадность. .+ съест больше, чем вы ожидаете. Думайте, не нужна ли ленивая форма .+?.

  • Производительность. Для огромных текстов плохо составленный regex может работать секундами. Компилируйте шаблоны, избегайте вложенных * внутри *.

  • Юникод. В Python 3 \w по умолчанию матчит и кириллицу. Если нужно только латиницу — используйте [A-Za-z0-9_].


Смотрите также


Примечание

Лицензия и источники

Часть материала адаптирована из официальной документации Python (https://docs.python.org/3/library/re.html и https://docs.python.org/3/howto/regex.html), доступной под Python Software Foundation License Version 2 (PSF License). Адаптация, переработка, оригинальные примеры и пояснения — © AlashEd Wiki.