в чем разница между ?:, ?! и? = в регулярном выражении?

108

Я искал значения этих выражений, но не мог понять точной разницы между ними. Вот что они говорят:

  • ?: Сопоставьте выражение, но не фиксируйте его.
  • ?= Сопоставьте суффикс, но исключите его из захвата.
  • ?! Соответствует, если суффикс отсутствует.

Я пробовал использовать их в простом RegEx и получил аналогичные результаты для всех. Пример: следующие 3 выражения дают очень похожие результаты.

  • [a-zA-Z0-9._-]+@[a-zA-Z0-9-]+(?!\.[a-zA-Z0-9]+)*
  • [a-zA-Z0-9._-]+@[a-zA-Z0-9-]+(?=\.[a-zA-Z0-9]+)*
  • [a-zA-Z0-9._-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9]+)*
РК Поддар
источник
Покажите нам свой тестовый пример. Они не должны давать одинаковых результатов.
Берги
@ sepp2k, это те же похожие результаты в нескольких случаях, один из которых упоминается в вопросе.
РК Поддар
@Bergi, я тестировал его со случайными данными, содержащими английские слова, номера телефонов, URL-адреса, адреса электронной почты, номера и т. Д.
Р.К. Поддар,
4
@RKAgarwal А, я вижу, что ты там делал. Вы добавили *после групп, поэтому они просто игнорируются.
sepp2k
Примечание для новичков : вы должны использовать их только в начале круглых скобок, а круглые скобки образуют группу захвата (разные наборы скобок извлекают разные разделы текста).
Райан Тейлор

Ответы:

156

Разница между ?=и ?!заключается в том, что первое требует, чтобы данное выражение соответствовало, а второе требует, чтобы оно не совпадало. Например, a(?=b)будет соответствовать «a» в «ab», но не «a» в «ac». Тогда как a(?!b)будет соответствовать «a» в «ac», но не «a» в «ab».

Разница между ?:и ?=заключается в том, ?=что выражение исключается из всего совпадения, ?:но не создается группа захвата. Так, например, a(?:b)будет соответствовать «ab» в «abc», тогда как a(?=b)будет соответствовать только «a» в «abc». a(b)совпадет с «ab» в «abc» и создаст захват, содержащий «b».

sepp2k
источник
81
?:  is for non capturing group
?=  is for positive look ahead
?!  is for negative look ahead
?<= is for positive look behind
?<! is for negative look behind

Пожалуйста, проверьте здесь: http://www.regular-expressions.info/lookaround.html, чтобы найти очень хорошее руководство и примеры просмотра вперед в регулярных выражениях.

анубхава
источник
15
И все же JavaScript не умеет смотреть назад.
Берги
1
Это более полно для общего регулярного выражения.
Ян
/ (? <= ^ a) b / у меня работало в javascript! Похоже, что в Интернете нет учебника по поиску отставания в Javascript.
Y. Yoshii
Только последние версии браузеров начали поддерживать поиск назад в JS
анубхава
- anubhava Я не знаю альтернативы / (? <= ^ A) b / с использованием чистого регулярного выражения. Возможно, я смогу, но мне придется полагаться на функции обратного вызова.
Y. Yoshii
21

Чтобы лучше понять, давайте применим три выражения плюс группу захвата и проанализируем каждое поведение.

  • () группа захвата - регулярное выражение внутри круглых скобок должно быть сопоставлено, и совпадение создает группу захвата
  • (?:) группа без захвата - регулярное выражение внутри круглых скобок должно совпадать, но не создает группу захвата
  • (?=) позитивный взгляд в будущее - утверждает, что регулярное выражение должно быть сопоставлено
  • (?!) отрицательный взгляд в будущее - утверждает, что невозможно сопоставить регулярное выражение

Подаем заявку q(u)iна выход . qсоответствует q, а группа захвата uсоответствует u . Будет выполнено совпадение внутри группы захвата, и группа захвата будет создана. Итак, двигатель продолжает работать i. И iбудет соответствовать i . Эта последняя попытка совпадения успешна.qui сопоставляется, и создается группа захвата с u .

Подаем заявку q(?:u)iна выход . Опять же, qсоответствует q, а группа без захвата uсоответствует u . Выбирается совпадение из группы без захвата, но группа захвата не создается. Итак, двигатель продолжает работать i. И iбудет соответствовать я . Эта последняя попытка совпадения успешна. qui совпадает

Подаем заявку q(?=u)iна выход . Предварительный просмотр положительный, за ним следует другой токен. Опять же, qсоответствует q и uсоответствует u . Опять же, совпадение из опережающего просмотра должно быть отброшено, поэтому движок отступает от iстроки к u . Предварительный просмотр был успешным, поэтому двигатель продолжает работу i. Ноi не может соответствовать тебе . Итак, эта попытка совпадения не удалась.

Подаем заявку q(?=u)uна выход . Предварительный просмотр положительный, за ним следует другой токен. Опять же, qсоответствует q и uсоответствует u . Совпадение из опережающего просмотра должно быть отброшено, поэтому движок отступает от uстроки к u . Предварительный просмотр был успешным, поэтому двигатель продолжает работу u. И uбудет соответствовать U . Итак, эта попытка совпадения успешна.qu соответствует

Подаем заявку q(?!i)uна выход . Даже в этом случае просмотр вперед положительный (потому iчто не совпадает), и за ним следует другой токен. Опять же, qсоответствует q и iне соответствует u . Совпадение из опережающего просмотра должно быть отброшено, поэтому движок отступает от uстроки к u . Предварительный просмотр был успешным, поэтому двигатель продолжает работу u. И uбудет соответствовать U . Итак, эта попытка совпадения успешна.qu соответствует

Итак, в заключение, реальная разница между прогнозирующими и не захватывающими группами заключается в том, хотите ли вы просто проверить наличие или проверить и сохранить совпадение. Группы захвата дороги, поэтому используйте их с умом.

Freedev
источник
> так что движок отступает от i в строке до u. Предварительный просмотр прошел успешно, поэтому движок продолжает работать с i. Но я не могу соответствовать тебе. ЭТО полностью сбивает с толку. Зачем отступать, если это опережающий взгляд ?
Зеленый
1
@Green. Важно понимать, что упреждающий просмотр и другие конструкции обходного направления заключаются в том, что, хотя они проходят через движения, чтобы увидеть, может ли их подвыражение совпадать, они фактически не «потребляют» какой-либо текст. Это может немного сбить с толку
freedev
7

Попробуйте сопоставить foobarс ними:

/foo(?=b)(.*)/
/foo(?!b)(.*)/

Первое регулярное выражение будет соответствовать и вернет «bar» в качестве первого (?=b)подчиненного совпадения - соответствует 'b', но не использует его, оставляя его в следующих скобках.

Второе регулярное выражение НЕ будет соответствовать, потому что оно ожидает, что за "foo" будет что-то отличное от 'b'.

(?:...)имеет тот же эффект, что и простой (...), но не возвращает эту часть в качестве подчиненного совпадения.

Lanzz
источник
0

Самый простой способ понять утверждения - рассматривать их как команду, вставленную в регулярное выражение. Когда движок работает с утверждением, он немедленно проверяет состояние, описанное в утверждении. Если результат верен, продолжайте выполнение регулярного выражения.

BlackGlory
источник
0

Вот настоящая разница:

>>> re.match('a(?=b)bc', 'abc')
<Match...>
>>> re.match('a(?:b)c', 'abc')
<Match...>

# note:
>>> re.match('a(?=b)c', 'abc')
None

Если вам все равно, содержимое после "?:" Или "? =", "?:" И "? =" Одинаковы. Оба они подходят для использования.

Но если вам нужен этот контент для дальнейшей обработки (а не просто для соответствия целому. В этом случае вы можете просто использовать «a (b)»), вы должны вместо этого использовать «? =». Причина "?:" Будет просто через это прочь.

Чайник
источник