Я довольно достоин с регулярными выражениями, и теперь я пытаюсь еще раз понять предвидение и lookbehind утверждения. Они главным образом имеют смысл, но я не совсем уверен, как порядок влияет на результат. Я смотрел на этот сайт, который помещает lookbehinds перед выражением и предвидениями после выражения. Мой вопрос, это изменяет что-нибудь? Недавний ответ здесь на ТАК помещенном предвидение перед выражением, которое является ведущим к моему беспорядку.
Когда учебные пособия вводят поиск, они, как правило, выбирают самый простой вариант использования для каждого из них. Поэтому они будут использовать такие примеры как (? ('b' не предшествует 'a') или
q(?=u)
('q', за которым следует 'u'). Это просто для того, чтобы не загромождать объяснение отвлекающими деталями, но это имеет тенденцию создавать (или усиливать) впечатление, что lookbehinds и lookaheads должны появляться в определенном порядке. Мне понадобилось довольно много времени, чтобы смириться с этой идеей, и я видел несколько других, которые тоже были этим затронуты.
Попробуйте взглянуть на более реалистичные примеры. Один из вопросов, который часто возникает, это проверка пароля; например, удостовериться, что новый пароль состоит как минимум из шести символов и содержит как минимум одну букву и одну цифру. Одним из способов сделать это может быть:
^(?=.*[A-Za-z])(?=.*\d)[A-Za-z0-9]{6,}$
Символьный класс [A-Za-z0-9]{6,}
может соответствовать всем буквам или всем цифрам, поэтому вы используете бланки просмотра, чтобы убедиться, что в пароле есть хотя бы по одной из них. В этом случае вы должны сначала сделать заголовки }, потому что более поздние части регекса должны быть способны исследовать всю строку.
Например, предположим, что необходимо найти все вхождения слова "там", если только ему не предшествует кавычки. Очевидным регексом для этого является (?, но если вы ищете большой корпус, то это может создать проблему с производительностью. Как написано, этот регекс сделает отрицательный взгляд на каждую позицию в тексте, и только тогда, когда это удастся, он проверит остальную часть регекса.
У каждого регекс-двигателя есть свои сильные и слабые стороны, но одно верно для всех из них - они быстрее находят фиксированные последовательности буквальных символов, чем что-либо другое - чем дольше последовательность, тем лучше. Это означает, что можно значительно быстрее сделать lookbehind last, хотя это означает двойное совпадение слова:
[Tt]here\b(?<!"[Tt]here)
Так что правило, регулирующее размещение lookaround'ов, заключается в том, что нет правила; вы помещаете их везде, где они имеют наибольший смысл в каждом случае.
1 (? = ABC)
означает поиск 1
, а совпадение (но не захватывать) ABC
после этого.
(? <= ABC) 1
означает матч (но не захватывать) ABC
до текущего местоположения и продолжать совпадать 1
.
Так что, как правило, вы будете поместите полей после выражения и поиска перед ним.
Когда мы размещаем вид после выражения, мы перепроверяют строку, которую мы уже подобрали . Это распространено, когда у вас есть сложные условия (вы можете подумать об этом как в и
Regexs). Например, посмотрите на этот недавний ответ Daniel Brückner :
.&.(?<! & )
Во-первых, вы захватите амперса и между двумя персонажами. Далее вы проверяете, что они не были пробелами ( \ S & \ S
не будет работать здесь, OP хотел захватить 1 & _
).
На примере легче показать, чем объяснить, я думаю. Давайте возьмем это Regex:
(?<=\d)(?=(.)\1)(?!p)\w(?<!q)
Что это значит:
(? <= \ D)
- Убедитесь, что то, что происходит до того, как положение совпадения является цифрой. (? = (.) \ 1)
- Убедитесь, что любой символ, который мы совпадаем на этой (той же) положении, сопровождается копией самого себя (через Backreference). (?! P)
- Убедитесь, что следует, что не является P
. \ W
- Сопоставьте букву, цифру или подчеркивание. Обратите внимание, что это первый раз, когда мы на самом деле сопоставляем и потребляем персонаж. (? - Убедитесь, что то, что мы до сих пор не заканчиваются Q
.
Все это будет сопоставить строки, такие как abc5ddx
или 9xx
, но не 5d
или 6QQ
или ASD6PP
или Добавить
. Обратите внимание, что каждое утверждение работает независимо. Он просто останавливается, оглядывается, и, если все хорошо, позволяет совпадать, чтобы продолжить.
Обратите внимание также, что в большинстве (возможно всех) реализациях, внешний вид превышает ограничение фиксированной длины. Вы не можете использовать операторы повторения / дополнительных, таких как ?
, *
, а +
в них. Это связано с тем, что для соответствия шаблону нам нужна отправная точка - в противном случае нам придется попробовать соответствовать каждому благу с каждой точки в строке.
Образец прогона этого регеляции на строке A3B5DDX
следующим образом:
\ D
всегда совпадает с 1 символом). Мы не можем совпадать с отрицательными показателями, так что потерпите неудачу и продвигайте курсор. A
не совпадает \ D
, поэтому выключите и продвиньте курсор снова. 3
соответствует \ D
, поэтому держите курсор в целом и продолжайте совпадение. B
совпадения (.) (.) [)
и захвачено. 5
не соответствует \ 1
(который является захваченным B
). Следовательно, терпят неудачу и продвигайте курсор.
B
не совпадают \ D
, поэтому выключите и продвиньте курсор снова. 5
. Соответствуйте \ d
, поэтому сохраняйте курсор в целом и продолжайте соответствие. D
совпадает (.)
и захвачено. Второе d
d соответствует \ 1
(который является первым захваченным D
). Разрешить соответствие продолжить, откуда мы остановились. B
в положении 4 не совпадает P
, и поскольку это негативно, как мы хотим; Разрешить соответствие продолжить. \ W
в положении 4. b
совпадения. Проверьте курсор, поскольку мы потребляли персонажа и продолжаем. Также отметьте это как начало матча. Q
всегда совпадает с 1 символом). D
не совпадает Q
, который мы хотим от негативного взгляда. d
.