Опережающие и ретроспективные проверки
Проверяем, что стоит до или после совпадения, не захватывая это в результат.
Lookaround — проверка контекста: совпадение засчитывается, только если рядом есть (или нет) нужный текст, но сам этот текст в результат не входит.
Зачем нужны проверки
Иногда совпадение должно зависеть от окружения. «Число, за которым идёт слово руб» — но само «руб» в результат брать не нужно. Обычные группы тут не подходят: они захватили бы лишнее. Решение — проверки нулевой ширины (lookaround): они смотрят на контекст, но не «съедают» символы.
Опережающая проверка (lookahead)
(?=...)— позитивный lookahead: дальше ДОЛЖЕН идти такой текст.(?!...)— негативный lookahead: дальше НЕ должно быть такого текста.
import re
text = "100 руб, 200 usd, 300 руб"
print(re.findall(r"\d+(?= руб)", text))
Вывод:
['100', '300']
Паттерн \d+(?= руб) совпал только с числами, за которыми идёт « руб». При этом само « руб» в результат не вошло — это и есть «нулевая ширина». Число 200 (usd) отсеялось.
Ретроспективная проверка (lookbehind)
(?<=...)— позитивный lookbehind: перед совпадением ДОЛЖЕН быть такой текст.(?<!...)— негативный lookbehind: перед совпадением НЕ должно быть такого текста.
import re
text = "Цена $100, скидка $20, бонус 50"
print(re.findall(r"(?<=\$)\d+", text))
Вывод:
['100', '20']
Здесь (?<=\$)\d+ ловит числа, перед которыми стоит знак $. Доллар не входит в совпадение — мы получили чистые числа 100 и 20, а 50 (без доллара) отсеялось.
Практика: разделитель тысяч
Эффектный приём — расставить пробелы по разрядам в большом числе, комбинируя lookbehind и lookahead:
import re
print(re.sub(r"\B(?=(\d{3})+(?!\d))", " ", "1234567"))
Вывод:
1 234 567
Паттерн вставляет пробел в позициях, после которых число цифр кратно трём. Это классический пример мощи проверок: мы ничего не захватываем, а лишь помечаем нужные позиции.
Ограничение lookbehind
В Python lookbehind должен быть фиксированной длины: (?<=ab) можно, а (?<=a+) — нельзя (переменная длина даст ошибку). Lookahead такого ограничения не имеет. Это особенность реализации, о которой стоит помнить.
Итог
- Lookaround проверяет контекст, не включая его в совпадение (нулевая ширина).
(?=...)/(?!...)— позитивный/негативный lookahead (что идёт после).(?<=...)/(?<!...)— позитивный/негативный lookbehind (что идёт до).- В Python lookbehind обязан быть фиксированной длины.