Более подробно эта тема рассматривается в курсе Python Fundamentals
В теме анкоров (или якорей) была рассмотрена возможность использования паттерна (шаблона) относительно начала или конца строки, или начала или конца слова.
С помощью regex lookaround, тоже самое можно сделать в любой части строки или слова. Причём в конечном итоге, и что ОЧЕНЬ важно, символы на которые ссылается lookaround НЕ БУДУТ добавлены в итоговый результат.
Анализ символов может быть как непосредственно перед шаблоном - метод lookahead(смотреть вперёд), так и за шаблоном lookbehind (посмотреть назад).
Кстати, в русскоязычной печати и блогосфере lookahead иногда называется “опережающая проверка” и lookbehind - “ретроспективная проверка”. Однако пользоваться аутентичными английскими терминами lookahead и lookbehind в данном случае будет более логично, и, что самое главное, более понятно.
Итак в чём же здесь суть? Попробуем разобрать это на примере.
Задача:
Допустим, нам необходимо найти имена, которые начинаются с _ и выбрать эти имена, отбросив нижнее подчёркивание.
Пример:
_id name list _name
На первый взгляд, самым простым решением будет: _\w+
Но ведь в задаче сказано, что имена нужны без подчёркиваний!
Другим относительно приемлемым решением будет выделить имя после нижнего подчёркивания в группу: _(\w+) И в Match information мы видим, что хотя и match не совпало с нужным нам результатом, но появились дополнительные группы с правильным ответом.
Однако, для того, чтобы получить правильный ответ в match, нам надо использовать lookaround. И, в частности, в этом конкретном случае - lookbehind (взгляд назад, поскольку поисковая машина проверяет символы слева-направо, поэтому переход в начало слова будет с точки зрения поискового алгоритма возвратом назад).
Для этого условие начала нижнего подчёркивания мы перенесём в конструкцию lookbehind:
(?<=_)\w+
Как видим, теперь нижнее подчёркивание вообще пропало из выделения, но зато в match появилось именно то, что нам нужно!
Это был пример positive lookbehind, когда наличие указанного элемента (или группы элементов) - истина.
Предположим, что условия изменились и теперь нам нужны слова, которые НЕ начинаются с нижнего подчёркивания.
В этом случае знак = мы меняем на !:
(?<!_)\w+
Как видим, это не дало ожидаемого результата: все слова выделились и стали валидными. Это произошло из-за того, что знак нижнего подчёркивания входит в \w, поэтому, для выполнения условия, выделение сместилось на знак влево. Изменим \w на [a-z]:
(?<!_)[a-z]+
Теперь выделение сместилось на символ вправо!
Чтобы исправить ситуацию добавим знак границы слова \b:
(?<:!_)\b[a-z]+
Теперь, благодаря negative lookbehind, всё работает как надо!
Аналогичную проверку можно выполнить для знака нижнего подчёркивания в КОНЦЕ слова. Для этого сначала изменим исходный текст:
Изменённый пример к предыдущей задаче:
id_ name list name_
Сначала попробуем выделить все слова: [a-z]+
Далее, с помощью positive lookahead (позитивного взгляда вперёд или более благозвучной, но менее понятной “позитивной упреждающей проверки”) оставим только те, что заканчиваются на нижнее подчёркивание:
[a-z]+(?=_)
Теперь, c помощью negative lookahead (негативный взгляд вперёд), попробуем наоборот, оставить только слова БЕЗ нижнего подчёркивания. И для этого знак (=) поменяем на (!).
Как видим, это помогло лишь отчасти: слова с подчёркиванием тоже пока выделены, правда, в них теперь выделен на один символ меньше. Чтобы их совсем убрать, жёстко ограничим конец слов границей \b:
[a-z]+\b(?!_)
Ура, получилось!
И ещё раз выведем выведем вместе все четыре рассмотренных варианта lookaround:
Pattern | Type of Lookaround |
---|---|
(?<=_)\w+ | positive lookbehind |
(?<!_)\w+ | negative lookbehind |
[a-z]+(?=_) | positive lookahead |
[a-z]+(?!_) | negative lookahead |