Регулярное выражение отрицательный взгляд
В моем домашнем каталоге у меня есть папка drupal-6.14, которая содержит платформу Drupal.
Из этого каталога я использую следующую команду:
find drupal-6.14 -type f -iname '*' | grep -P 'drupal-6.14/(?!sites(?!/all|/default)).*' | xargs tar -czf drupal-6.14.tar.gz
Эта команда делает gzips папку drupal-6.14, исключая все подпапки drupal-6.14/sites/ кроме sites/all и sites / default, которые она включает.
Мой вопрос по регулярному выражению:
grep -P 'drupal-6.14/(?!sites(?!/all|/default)).*'
Выражение работает для исключения всех папок, которые я хочу исключить, но я не совсем понимаю, почему.
Это обычная задача с использованием регулярных выражений для
Соответствует всем строкам, кроме тех, которые не содержат подшаблон x. Или, другими словами, отрицание подшаблона.
Я (думаю) понимаю, что общей стратегией для решения этих проблем является использование негативных взглядов, но я никогда не понимал до удовлетворительного уровня, как работают позитивные и негативные взгляды (впереди / позади).
За эти годы я прочитал много сайтов на них. Руководства по регулярным выражениям PHP и Python, другие страницы, такие как http://www.regular-expressions.info/lookaround.html и т. Д., Но у меня никогда не было четкого понимания их.
Может ли кто-нибудь объяснить, как это работает, и, возможно, привести несколько похожих примеров, которые бы делали подобные вещи?
- Обновите один:
Относительно ответа Andomar: может ли двойной отрицательный прогноз быть более кратко выражен в виде одного положительного прогнозного заявления:
то есть
'drupal-6.14/(?!sites(?!/all|/default)).*'
эквивалентно:
'drupal-6.14/(?=sites(?:/all|/default)).*'
???
- Обновление два:
Что касается @andomar и @alan moore - вы не можете поменять местами двойной отрицательный взгляд на положительный взгляд.
3 ответа
Отрицательный взгляд говорит, что в этой позиции следующее регулярное выражение может не совпадать.
Давайте возьмем упрощенный пример:
a(?!b(?!c))
a Match: (?!b) succeeds
ac Match: (?!b) succeeds
ab No match: (?!b(?!c)) fails
abe No match: (?!b(?!c)) fails
abc Match: (?!b(?!c)) succeeds
Последний пример - двойное отрицание: он позволяет b
с последующим c
, Вложенный отрицательный взгляд становится положительным взглядом: c
должен присутствовать.
В каждом примере только a
совпадает. Предварительный просмотр является только условием и не добавляет к сопоставленному тексту.
Lookarounds могут быть вложенными.
Таким образом, это регулярное выражение соответствует "drupal-6.14/", за которым не следует "sites", за которым не следует "/ all" или "/ default".
Смешение? Используя разные слова, мы можем сказать, что он соответствует "drupal-6.14/", за которым не следует "sites", если за ним не следует "/ all" или "/ default"
Если вы измените свое регулярное выражение следующим образом:
drupal-6.14/(?=sites(?!/all|/default)).*
^^
... тогда он будет соответствовать всем входам, которые содержат drupal-6.14/
с последующим sites
сопровождаемый чем-либо кроме /all
или же /default
, Например:
drupal-6.14/sites/foo
drupal-6.14/sites/bar
drupal-6.14/sitesfoo42
drupal-6.14/sitesall
изменения ?=
в ?!
чтобы соответствовать вашему исходному регулярному выражению, просто отменяет эти соответствия:
drupal-6.14/(?!sites(?!/all|/default)).*
^^
Итак, это просто означает, что drupal-6.14/
теперь не может сопровождаться sites
сопровождаемый чем-либо кроме /all
или же /default
, Итак, теперь эти входные данные будут удовлетворять регулярному выражению:
drupal-6.14/sites/all
drupal-6.14/sites/default
drupal-6.14/sites/all42
Но что может быть неочевидно из некоторых других ответов (и, возможно, вашего вопроса), так это то, что ваше регулярное выражение также разрешит другие входные данные, где drupal-6.14/
сопровождается чем-либо кроме sites
также. Например:
drupal-6.14/foo
drupal-6.14/xsites
Вывод: Итак, ваше регулярное выражение в основном говорит, чтобы включить все подкаталоги drupal-6.14
кроме тех подкаталогов sites
чье имя начинается с чего-либо, кроме all
или же default
,