Жадность и лень, границы слов \b и якоря ^ $

Понимаем, почему квантор хватает «слишком много», и учимся привязывать паттерн к нужным местам.

Жадный квантор берёт максимум символов; добавление ? делает его ленивым — он берёт минимум.

Жадность по умолчанию

Кванторы * + {} по умолчанию жадные: они захватывают как можно больше символов. Это часто удивляет новичков. Попробуем вытащить тег из HTML:

import re

print(re.findall(r"<.+>", "<b>текст</b>"))

Вывод:

['<b>текст</b>']

Мы хотели получить <b>, а получили всю строку! Жадная .+ схватила всё от первой < до последней >, включая текст и закрывающий тег.

Ленивый режим: добавляем ?

Чтобы квантор брал минимум, после него ставят ?. Теперь .+? остановится на первой же подходящей >:

import re

print(re.findall(r"<.+?>", "<b>текст</b>"))

Вывод:

['<b>', '</b>']

Вот теперь — два отдельных тега. Запомните пару: .+ — жадная (макс), .+? — ленивая (мин). То же касается *?, ??, {2,5}?.

Якоря ^ и $

Якоря не совпадают с символами — они привязывают паттерн к позиции. ^ — начало строки, $ — конец. Связка ^...$ требует, чтобы паттерн описывал строку целиком:

import re

print(bool(re.search(r"^\d+$", "12345")))   # строка целиком из цифр
print(bool(re.search(r"^\d+$", "12a45")))    # есть буква — нет

Вывод:

True
False

Это типовой способ валидации: ^...$ гарантирует, что вся строка соответствует шаблону, а не только её часть.

Граница слова \b

Граница слова \b — это позиция между символом-словом (\w) и не-словом (или краем строки). Она помогает искать целые слова, а не куски внутри других слов:

import re

print(re.findall(r"cat", "cat category scatter"))
print(re.findall(r"\bcat\b", "cat category scatter"))

Вывод:

['cat', 'cat', 'cat']
['cat']

Без границ cat нашёлся и в «category», и в «scatter». С \bcat\b — только отдельное слово «cat». Это спасает от ложных совпадений внутри слов.

Итог

  • Кванторы жадные по умолчанию: берут максимум. Добавьте ? для ленивого режима (минимум).
  • ^ — начало строки, $ — конец; связка ^...$ проверяет строку целиком.
  • \b — граница слова; \bслово\b ищет целое слово, а не подстроку.
Проверьте себя
1. Что вернёт findall с паттерном <.+> для строки <b>текст</b>?
AДва тега: <b> и </b>
BОдно совпадение — всю строку <b>текст</b>, потому что .+ жадная
CНичего
DТолько слово текст
2. Как сделать квантор .+ ленивым?
AНаписать .+!
BНаписать .+? — добавить вопросительный знак
CНаписать .+*
DЛенивым его сделать нельзя
3. Зачем нужна граница слова \b в паттерне \bcat\b?
AЧтобы найти любые слова с cat внутри
BЧтобы найти cat только как отдельное слово, а не часть category или scatter
CЧтобы искать без учёта регистра
DЧтобы ускорить поиск
Поддержать проект