Регулярное выражение отрицательное выражение
В моем домашнем каталоге у меня есть папка 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/ кроме сайтов /all и sites/default, которые она включает.
Мой вопрос заключается в регулярном выражении:
grep -P 'drupal-6.14/(?!sites(?!/all|/default)).*'
Выражение работает, чтобы исключить все папки, которые я хочу исключить, но я не совсем понимаю, почему.
Это обычная задача, использующая регулярные выражения для
Сопоставьте все строки, кроме тех, что не содержат подшаблон x. Или, другими словами, отрицание подшаблона.
Я (думаю) Я понимаю, что общая стратегия решения этих проблем - использование негативных взглядов, но я никогда не понимал удовлетворительного уровня, как работает позитивный и негативный внешний вид (вперед/назад).
На протяжении многих лет я читал на них множество веб-сайтов. Регулярные руководства PHP и Python, другие страницы, такие как http://www.regular-expressions.info/lookaround.html и т.д., Но я никогда не был действительно твердое понимание их.
Может кто-нибудь объяснить, как это работает, и, возможно, предоставить некоторые аналогичные примеры, которые будут делать похожие вещи?
- Обновить один:
Относительно ответа Andomar: может ли двойной негативный взгляд быть более кратко выражен как одно положительное выражение:
i.e:
'drupal-6.14/(?!sites(?!/all|/default)).*'
эквивалентно:
'drupal-6.14/(?=sites(?:/all|/default)).*'
???
- Обновить два:
В соответствии с @andomar и @alan moore - вы не можете обменивать двойной негативный прогноз для позитивного просмотра.
Ответы
Ответ 1
Отрицательный lookahead говорит, что в этой позиции следующее регулярное выражение не может совпадать.
Возьмем упрощенный пример:
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
. Вложенный негативный lookahead становится положительным: c
должен присутствовать.
В каждом примере сопоставляется только a
. Lookahead - это только условие и не добавляет согласованный текст.
Ответ 2
Взгляды могут быть вложенными.
Итак, это регулярное выражение соответствует "drupal-6.14/", которое не, за которым следуют "сайты", которые не, за которыми следуют "/all" или "/default".
Непонятный? Используя разные слова, мы можем сказать, что он соответствует "drupal-6.14/", который не, за которым следуют "сайты" , если, за которыми следуют "/all" или "/по умолчанию"
Ответ 3
Если вы измените свое регулярное выражение следующим образом:
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
.