Перейти к основному содержимому

Регулярные выражения — проверки вокруг совпадения

Разработчику

Предыдущий: группы и замена.
Следующий: флаги и жадность.


Идея простыми словами

Иногда нужно найти текст только при условии, что рядом что-то есть (или наоборот — чего-то нет). При этом соседний кусок не всегда нужно включать в результат.

Примеры:

  • цена после знака $, сам $ в ответ не нужен;
  • пароль: минимум 8 символов, и есть цифра, и есть заглавная буква;
  • слово cat, за которым не идёт s (чтобы отсечь cats).

Для этого служат проверки (lookaround): движок смотрит вперёд или назад, не сдвигая основной указатель и не добавляя проверку в захват (в отличие от обычных ()).


Четыре вида

ЗаписьНазваниеСмысл
(?=...)опережающая, положительнаядальше должно быть ...
(?!...)опережающая, отрицательнаядальше не должно быть ...
(?<=...)ретроспективная, положительнаяперед этим местом было ...
(?<!...)ретроспективная, отрицательнаяперед этим местом не было ...

Скобки (?:...) из прошлой статьи — обычная группа без захвата. Lookaround — отдельный механизм «подглядывания».


Опережающая проверка (?=...)

Текст:

Цена $42.50 и скидка $9

Нужны числа после доллара, без самого $ в совпадении:

(?<=\$)\d+(?:\.\d{2})?

В движках без фиксированной ширины lookbehind иногда пишут так:

(?<=\$)[0-9]+(?:\.[0-9]{2})?

Разбор (?<=\$):

ЧастьЗначение
(?&lt;=\$)прямо слева должен быть $
\d+одна или больше цифр
(?:\.\d{2})?необязательные копейки .50

Совпадения: 42.50 и 9.

Опережающий вариант (если lookbehind недоступен): \$(\d+(?:\.\d{2})?) — тогда $ попадёт в полное совпадение, а цифры — в группу 1.


«Должно быть дальше» — пароль

Требования:

  • длина от 8;
  • хотя бы одна заглавная латинская;
  • хотя бы одна цифра;
  • хотя бы один спецсимвол @$!%*?&;
  • только разрешённые символы.
^(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$

Читаем по блокам:

БлокЧто проверяет
^начало строки
(?=.*[A-Z])где-то дальше есть заглавная
(?=.*\d)где-то дальше есть цифра
(?=.*[@$!%*?&])где-то дальше есть спецсимвол
[A-Za-z\d@$!%*?&]\{8,\}основная часть: 8+ разрешённых символов
$конец строки

Каждый (?=...) не потребляет символы — только проверяет. Поэтому три условия можно поставить подряд.

ПарольРезультат
Abcdef1!подходит
abcdef1!нет заглавной
Abcdefghнет цифры и спецсимвола

Отрицательная опережающая (?!...)

Шаблон (?!...): «на этом месте не начинается то, что дальше в скобках».

Пример: слово The, после которого не идёт пробел и fat:

(T|t)he(?!\sfat)

В строке The fat cat совпадение на первом The отсечётся (после него как раз fat). Другие вхождения the без fat останутся.


Ретроспективная (?<=...)

Текст:

The fat cat sat on the mat.

Найти fat или mat, если перед ними было The или the :

(?<=(T|t)he\s)(fat|mat)
ЧастьЗначение
`(?<=(Tt)he\s)`
`(fatmat)`

Отрицательная ретроспективная (?<!...)

Найти cat, перед которым нет The / the :

(?<!(T|t)he\s)(cat)

В The cat sat on cat второе cat подойдёт, первое — нет.


Когда что выбирать

СитуацияПриём
Несколько независимых условий к одной строкенесколько (?=...) в начале
«Только если после идёт X»(?=X) или захват с группой
«Только если перед был X»(?&lt;=X)
Не засорять список групп(?:...) + lookaround

Ограничения

  • В старых движках (?<=...) требует фиксированной ширины (например (?<=ab) — да, (?<=a+) — нет). В JavaScript lookbehind появился относительно недавно.
  • Сложные цепочки (?=...)(?=...) усложняют чтение — для продакшена иногда проще проверить пароль тремя отдельными условиями в коде.

Дальше: Флаги и жадность →

См. также

Другие статьи этого же раздела в боковом меню (как на странице «О разделе»).