RegExp: lookaround
Lookahead и lookbehind, в совокупности называемые «lookaround», являются утверждениями нулевой длины, которые проверяют совпадение с указанными символами, но возвращают только наличие или отсутствие совпадения.
lookaround | Пример | Примечание |
---|---|---|
positive lookahead (?=…) |
~[\d]{1,6}(?= руб\.)~ поиск и захват сумм, выраженных в рублях |
используется при необходимости получить все совпадения с выражением, за которым следует определенный шаблон |
negative lookahead (?!…) |
~а(?!ла)~ поиск и захват подстроки ‘а’, непосредственно за которой не следует подстрока ‘ла’ |
используется, при необходимости получить все совпадения с выражением, за которым не следует определенный шаблон |
positive lookbehind (?<=…) |
~(?<=\$)[\d]{1,6}~ поиск и захват сумм, выраженных в долларах |
используется для получения всех совпадений, которым предшествует определенный шаблон |
negative lookbehind (?<!…) |
~(?<!упа)ла~ поиск и захват подстроки ‘ла’, не предворенной подстрокой ‘упа’ |
используется для получения всех совпадений, которым не предшествует определенный шаблон |
Внутри lockahead можно использовать любое регулярное выражение, в отличии от lookbehind здесь не будут работать группы захвата.
Очевидно, что позитивные взгляды возвращают true при наличии совпадения, отрицательные - при отсутствии такового.
И lookbehind смотрят совпадение перед захватываемым выражением, а lookahead - после. Пока все просто.
Сравнение: со Смотрителями или без них?
Смотрители являются мощным инструментом регулярных выражений, незаслуженно обойденным вниманием из-за своей сложности. Они позволяют комбинировать несколько требований к искомой подстроке, что порой превращается в нетривиальную задачу.
Например, для поиска слова из 4 символов, содержащего подстроку ‘con’. Без использования Смотрителей у меня получилось ‘~(^|\s)\wcat(\s|$)|(^|\s)cat\w(\s|$)~g‘.
И это для латиницы (используется \w) и всего для двух вариантов. При добавлении или усложнении условий выражение становится нечитабельным из-за своих размеров. Кроме того, полученные совпадения необходимо тримить от захваченных пробельных символов.
С использованием Смотрителей: ‘~(?=(^|\s))(?=.*cat)\w{4}(?=(\s|$))~gm‘.
Попытаюсь расписать полученное выражение:
- ‘
(?=(^|\s))
‘ - Позитивный Смотритель Вперёд проверяет наличие перед искомым выражением пробельного символа либо начала строки; - ‘
(?=.*cat)
‘ - Второй Позитивный Смотритель Вперёд проверяет наличие в искомой строке подстроки ‘cat’, без указания позиции; - ‘
\w{4}
‘ - непосредственно поиск подстроки, содержащей 4 символа \w; - ‘
(?=(\s|$))
‘ - Третий Позитивный Смотритель Вперёд производит поиск пробельного символа либо конца строки за искомой строкой.
Пример: валидация пароля
Составим выражение для сложной валидации паролей, которая должна учесть минимальное количество символов, наличие минимум одной цифры, минимум одной заглавной и минимум одной прописной латинской буквы.
В данном регулярном выражении используется positive lookahead - Позитивный Смотритель Вперёд, ищущий наличие в строке трех выражений, причем каждое выражение начинается с необязательных символов - будет найдено в любой позиции строки. В последней части проверяется количество символов и отсеиваются строки с запрещенными символами:
// UsersController.php or UserRequest ... request()->validate([ 'new_password' => [ 'string', 'min:8', 'max:255', 'regex:~(?=.*\d)(?=.*[a-z])(?=.*[A-Z])[\da-zA-Z]{8,}~', 'confirmed' ], 'password' => 'string', ]);
тема сисек пока не раскрыта. Нет atomic groups, описание скудное. Постараюсь исправить.
когда? я фанат, я волна!