Regex lookahead заказ - PullRequest
       0

Regex lookahead заказ

6 голосов
/ 24 января 2010

Я довольно приличен с регулярными выражениями, и теперь я пытаюсь еще раз понять утверждения с предвзятостью и взглядом за спиной. В основном они имеют смысл, но я не совсем уверен, как порядок влияет на результат. Я смотрел на этот сайт , который размещает взгляды перед выражением и взгляды после выражения. У меня вопрос, это что-то меняет? Недавний ответ здесь на SO поставил точку зрения перед выражением, которое приводит меня в замешательство.

Ответы [ 3 ]

9 голосов
/ 24 января 2010

Когда в учебниках вводятся обходные пути, они, как правило, выбирают самый простой вариант использования для каждого из них. Таким образом, они будут использовать примеры, такие как (?<!a)b («b» не предшествует «a») или q(?=u) («q», за которым следует «u»). Это просто для того, чтобы не загромождать объяснение отвлекающими деталями, но оно имеет тенденцию создавать (или усиливать) впечатление, что взгляды за спиной и впереди должны появляться в определенном порядке. Мне потребовалось довольно много времени, чтобы избавиться от этой идеи, и я видел, как несколько других тоже страдали от нее.

Попробуйте взглянуть на несколько более реалистичных примеров. Один вопрос, который часто возникает, касается проверки паролей; например, убедитесь, что новый пароль имеет длину не менее шести символов и содержит как минимум одну букву и одну цифру. Один из способов сделать это будет:

^(?=.*[A-Za-z])(?=.*\d)[A-Za-z0-9]{6,}$

Класс символов [A-Za-z0-9]{6,} может совпадать со всеми буквами или всеми цифрами, поэтому вы используете предпросмотры, чтобы убедиться, что есть хотя бы одна из них. В этом случае вы должны выполнить предварительные запросы first , потому что более поздние части регулярного выражения должны быть в состоянии исследовать всю строку.

В качестве другого примера, предположим, что вам нужно найти все вхождения слова "там", если ему не предшествует кавычка. Очевидное регулярное выражение для этого - (?<!")[Tt]here\b, но если вы ищете большой корпус, это может создать проблему производительности. Как написано, это регулярное выражение будет негативно смотреть на каждую позицию в тексте, и только когда это удастся, оно будет проверять оставшуюся часть регулярного выражения.

Каждый движок регулярных выражений имеет свои сильные и слабые стороны, но одна вещь, которая верна для всех из них, заключается в том, что они быстрее находят фиксированные последовательности буквенных символов, чем что-либо еще - чем длиннее последовательность, тем лучше. Это означает, что может быть значительно быстрее выполнить просмотр за последним , даже если это означает совпадение слова дважды:

[Tt]here\b(?<!"[Tt]here)

Таким образом, правило, регулирующее размещение lookarounds, состоит в том, что правила не существует; Вы помещаете их туда, где они имеют смысл в каждом конкретном случае.

4 голосов
/ 24 января 2010

Проще показать в примере, чем объяснить, я думаю. Давайте возьмем это регулярное выражение:

(?<=\d)(?=(.)\1)(?!p)\w(?<!q)

Что это значит:

  1. (?<=\d) - убедитесь, что перед позицией совпадения стоит цифра.
  2. (?=(.)\1) - убедитесь, что за любым символом, которому мы сопоставляем в этой (той же) позиции, следует его копия (через обратную ссылку).
  3. (?!p) - убедитесь, что следующее не является p.
  4. \w - соответствует букве, цифре или подчеркиванию. Обратите внимание, что это первый раз, когда мы действительно сопоставляем и потребляем персонажа.
  5. (?<!q) - убедитесь, что то, что мы сопоставили до сих пор, не заканчивается q.

Все это будет соответствовать строкам типа abc5ddx или 9xx, но не 5d или 6qq или asd6pp или add. Обратите внимание, что каждое утверждение работает независимо. Он просто останавливается, осматривается и, если все хорошо, позволяет продолжить сопоставление.

Обратите внимание также, что в большинстве (вероятно, во всех) реализациях ограничение просмотра имеет ограничение фиксированной длины. В них нельзя использовать операторы повторения / необязательности, такие как ?, * и +. Это связано с тем, что для сопоставления с шаблоном нам нужна начальная точка - в противном случае мы должны попытаться сопоставить каждый взгляд позади каждой точки строки.

Пример выполнения этого регулярного выражения в строке a3b5ddx выглядит следующим образом:

  1. Положение текстового курсора: 0.
    1. Попробуйте сопоставить первый взгляд сзади в позиции -1 (поскольку \d всегда соответствует 1 символу). Мы не можем найти совпадения при отрицательных индексах, поэтому проваливаем и перемещаем курсор.
  2. Положение текстового курсора: 1.
    1. Попробуйте сопоставить первый взгляд сзади в позиции 0. a не соответствует \d, поэтому не удалось и снова переместите курсор.
  3. Положение текстового курсора: 2.
    1. Попробуйте сопоставить первый вид сзади в позиции 1. 3 соответствует \d, поэтому не трогайте курсор и продолжайте сопоставлять.
    2. Попробуйте сопоставить первого взгляда в позиции 2. b соответствует (.) и захвачено. 5 не соответствует \1 (который является захваченным b). Таким образом, сбой и продвижение курсора.
  4. Положение текстового курсора: 3.
    1. Попробуйте сопоставить первый взгляд сзади в позиции 2. b не соответствует \d, поэтому не пройдитесь и снова переместите курсор.
  5. Положение текстового курсора: 4.
    1. Попробуйте сопоставить первый вид сзади в позиции 3. 5 соответствует \d, поэтому не трогайте курсор и продолжайте сопоставлять.
    2. Попробуйте сопоставить первый взгляд в позиции 4. d соответствует (.) и захвачен. Второй d соответствует \1 (который является первым захваченным d). Разрешить сопоставление продолжить с того места, где мы остановились.
    3. Попробуйте сопоставить второй взгляд. b в позиции 4 не совпадает с p, и, поскольку это негативная перспектива, мы этого и хотим; разрешить сопоставление продолжить.
    4. Попробуйте сопоставить \w в позиции 4. b совпадений. Переместите курсор вперед, так как мы использовали символ и продолжаем. Также отметьте это как начало матча.
  6. Положение текстового курсора: 5.
    1. Попробуйте сопоставить второй взгляд сзади в позиции 4 (поскольку q всегда соответствует 1 символу). d не соответствует q, что мы хотим от негативного взгляда сзади.
    2. Поймите, что мы находимся в конце регулярного выражения, и сообщите об успехе, вернув подстроку с начала матча в текущую позицию (от 4 до 5), которая равна d.
1 голос
/ 24 января 2010

1(?=ABC) означает - ищите 1 и сопоставляйте (но не захватывайте) ABC после него.
(?<=ABC)1 означает - сопоставить (но не перехватить) ABC до текущего местоположения и продолжить сопоставление 1.
Поэтому, как правило, вы ставите предвидение после выражения и заглядывание за ним.

Когда мы помещаем взгляд назад после выражения, мы перепроверяем строку, с которой мы уже соответствовали . Это часто случается, когда у вас сложные условия (вы можете думать об этом как о AND регулярных выражений). Например, посмотрите на этот недавний ответ Даниэль Брюкнер :

.&.(?<! & )

Сначала вы захватываете амперсанд между двумя персонажами. Затем вы проверяете, что оба они не являются пробелами (\S&\S здесь не сработает, ОП хотел захватить 1&_).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...