Блог

Регулярные Выражения. Regex Lookaround (Проверка окружения шаблона)

Более подробно эта тема рассматривается в курсе 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

<<< Назад к началу статьи "Регулярные Выражения. Предисловие"