Регулярные выражения
Теория
Регулярное выражение — короткая запись шаблона текста: «две заглавные буквы, дефис, четыре цифры». С его помощью проверяют формат (артикул, телефон, промокод), находят нужные куски в тексте (номера заказов в логе), извлекают части и заменяют их. В Python для этого модуль re. Строковых методов (split, startswith, in) хватает для простых случаев; регулярное выражение нужно, когда структура текста сложнее «найти подстроку».
Шаблоны пишут в сырых строках r"...", чтобы обратный слеш (\d — цифра) не обрабатывался самим Python. Главная путаница — между функциями: re.fullmatch проверяет, что вся строка подходит под шаблон, re.search ищет совпадение где угодно внутри, re.findall возвращает все совпадения, re.sub заменяет. Для проверки формата нужен fullmatch — иначе «мусор» вокруг правильного фрагмента пройдёт проверку. Регулярки легко сделать нечитаемыми: если шаблон не помещается в голове, лучше разбить задачу или обойтись методами строк.
Наглядно
Таблица 1. Основные элементы шаблона.
| Элемент | Значит | Пример | Совпадает с |
|---|---|---|---|
\d, \D |
цифра, не цифра | \d{4} |
0142 |
\w, \s |
буква/цифра/_, пробельный символ |
\w+ |
SALE10 |
[A-Z], [^0-9] |
символ из набора, не из набора | [A-Z]{2} |
KB |
. |
любой символ (кроме перевода строки) | a.c |
abc, a-c |
\. |
именно точка | \d+\.\d{2} |
1499.90 |
{n}, {n,m}, +, *, ? |
повторения: ровно n, от n до m, 1+, 0+, 0 или 1 | \d{1,3} |
7, 42, 100 |
^, $ |
начало и конец строки | ^SALE |
SALE10 |
( ) |
группа — часть, которую можно извлечь | №(\d+) |
№1042 → 1042 |
*?, +? |
«ленивое» повторение — как можно меньше | "(.*?)" |
каждое значение в кавычках отдельно |
Таблица 2. Функции модуля re.
| Функция | Что делает | Результат |
|---|---|---|
re.fullmatch(p, s) |
вся строка соответствует шаблону | объект совпадения или None |
re.search(p, s) |
первое совпадение где угодно | объект совпадения или None |
re.findall(p, s) |
все совпадения | список строк (или групп) |
re.sub(p, repl, s) |
заменить все совпадения | новая строка |
m.group(1) |
содержимое группы совпадения | строка |
На практике
Задача: проверять формат артикула и цены, вытаскивать номера заказов из лога, приводить телефоны к одному виду.
Шаг 1. Проверяем формат целиком
Артикул: две заглавные латинские буквы, дефис, четыре цифры, дефис, три буквы цвета:
import re SKU = r"[A-Z]{2}-\d{4}-[A-Z]{3}" for sku in ("KB-0142-BLK", "kb-0142-blk", "KB-142-BLK"): print(sku, bool(re.fullmatch(SKU, sku)))
Результат:
KB-0142-BLK True kb-0142-blk False KB-142-BLK False
Шаг 2. Отличаем search от fullmatch
search ищет подходящий кусок где угодно — для проверки формата это дыра:
bad = "XXKB-0142-BLK!!" print(bool(re.search(SKU, bad)), bool(re.fullmatch(SKU, bad)))
Результат:
True False
Шаг 3. Извлекаем части группами
Скобки выделяют группы — их содержимое можно достать после совпадения:
m = re.fullmatch(r"([A-Z]{2})-(\d{4})-([A-Z]{3})", "KB-0142-BLK") print(m.group(1), m.group(2), m.group(3)) print(int(m.group(2)))
Результат:
KB 0142 BLK 142
Группы всегда возвращают строки — число получают явным int().
Шаг 4. Находим все совпадения
Номера заказов из строки лога. С одной группой findall возвращает содержимое групп:
log = "заказ №1042 оплачен; заказ №1043 отменён; возврат по №1040" print(re.findall(r"№(\d+)", log))
Результат:
['1042', '1043', '1040']
Шаг 5. Заменяем лишнее
Телефон из формы приводим к виду «только цифры» — заменяем всё, что не цифра, на пустую строку:
for phone in ("+7 (912) 345-67-89", "8-912-345-67-89"): print(re.sub(r"\D", "", phone))
Результат:
79123456789 89123456789
Шаг 6. Экранируем специальные символы
Точка в шаблоне — «любой символ». Чтобы найти именно точку, её экранируют:
for price in ("1499.90", "1499,90", "1499x90"): print(price, bool(re.fullmatch(r"\d+.\d{2}", price)), bool(re.fullmatch(r"\d+\.\d{2}", price)))
Результат:
1499.90 True True 1499,90 True False 1499x90 True False
Шаг 7. Ограничиваем жадность
.* захватывает как можно больше — до последней подходящей кавычки. .*? — как можно меньше:
text = 'В корзине "Мышь" и "Коврик"' print(re.findall(r'"(.*)"', text)) print(re.findall(r'"(.*?)"', text))
Результат:
['Мышь" и "Коврик'] ['Мышь', 'Коврик']
Частые ошибки
searchдля проверки формата. Пропускает строки с мусором вокруг правильного фрагмента. Для проверки —fullmatch.- Неэкранированная точка.
\d+.\d{2}принимает1499,90и1499x90. Нужна\.. - Жадное
.*. Захватывает до последнего совпадения — сразу несколько значений. Используйте.*?или более точный набор ([^"]*). - Строка без
r."\d"пока работает, но Python предупреждает о неизвестной escape-последовательности, а"\b"— это символ backspace, а не граница слова. Шаблоны — всегдаr"...". - Число из группы.
m.group(2)— строка"0142"; для вычислений нуженint(). - Регулярка для всего. Email, URL, HTML честно разбираются только специальными инструментами; для простых задач хватает методов строк.
Итог
Шаблоны
- Сырые строки
r"...";\d,\w,\s, наборы[...], повторения{n,m},+,*,?. .— любой символ; точка как символ —\..- Группы
( )выделяют извлекаемые части;*?— ленивое повторение.
Функции
fullmatch— проверка формата всей строки;search— поиск где угодно.findall— все совпадения (с группой — содержимое группы);sub— замена.- Результат групп — строки.
Меры предосторожности
- Для проверки — только
fullmatch. - Сложный шаблон — повод упростить задачу.
Вопросы на собеседовании
Чем re.match, re.search и re.fullmatch отличаются?
search ищет совпадение в любом месте строки, match — только с начала строки (но не обязательно до конца), fullmatch — требует, чтобы под шаблон подходила вся строка. Для проверки формата нужен fullmatch.
Что такое жадные и ленивые квантификаторы?
Жадные (*, +) захватывают как можно больше символов, ленивые (*?, +?) — как можно меньше. На "a" и "b" шаблон "(.*)" захватит a" и "b, а "(.*?)" — отдельно a и b.
Зачем писать шаблоны в сырых строках?
В обычной строке Python сам обрабатывает обратный слеш: "\b" превратится в символ backspace, а не в границу слова, "\d" вызовет предупреждение. В сырой строке r"\b" обратный слеш доходит до модуля re как есть.