Есть ли способ сделать отрицательный просмотр в регулярном выражении vim?

Есть ли в Vim способ поиска строк, которые соответствуют, скажем, abc, но не содержат xyz позже в строке? Таким образом, следующие строки будут соответствовать:

The abc is the best
The first three letters are abc

и следующее не будет соответствовать:

The abc is the best but xyz is cheaper
The first three letters are abc and the last are xyz

Я знаю о синтаксисе, подобном следующему:

/abc\(xyz\)\@!

но это только позволяет избежать совпадения abcxyz, а не если между ними есть что-то среднее, например abc-xyz. С использованием

/abc.*\(xyz\)\@!

также не работает, потому что в строке есть много позиций, где xyz не соответствует.

(Я должен отметить, что в командной строке я бы сделал что-то вроде grep abc <infile | grep -v xyz, но я хотел бы сделать это интерактивно в Vim.)


person Greg Hewgill    schedule 15.01.2014    source источник


Ответы (5)


Ваша попытка была довольно близка; вам нужно вытащить .*, который допускает произвольное расстояние между совпадением и заявленным позже несоответствием в отрицательный прогноз:

/abc\(.*xyz\)\@!

Я предполагаю, что это работает, потому что несоответствие предпринимается для всех возможных совпадений .*, и только когда все ветки исчерпаны, \@! объявляется выполненным.

person Ingo Karkat    schedule 15.01.2014
comment
Это эквивалентно abc(?!.*xyz) в Perl-подобных движках. - person nhahtdh; 16.01.2014
comment
И это работает, потому что простой просмотр вперед запускает еще один полномасштабный поиск шаблона из текущей позиции и использует результат, чтобы решить, продолжать или нет (в зависимости от того, является ли просмотр вперед положительным или отрицательным). - person nhahtdh; 16.01.2014
comment
также см. :help perl-patterns. Для pattern2, которому не предшествует pattern1, то есть отрицательный просмотр назад, найдите \(pattern1\)\@<!pattern2. - person hochl; 22.02.2017
comment
Это может быть устаревшим, но мне кажется, что приведенное выше решение не будет работать для такой строки, как xyzabc. ОП указал, что в строке не должно быть xyz нигде в строке. Что сработало для меня, так это захват всей строки: ^(?!.*xyz).*abc(?!.*xyz) (не формат vim). См. RegExr. - person Guy; 25.12.2017
comment
Это помогло мне найти строки изменений в плане Terraform /\("[^"]*"\) => \(\1\)\@! - person Bruno Bronosky; 30.07.2019

Мне всегда кажется странным, что вам нужно экранировать скобки в vim, поэтому большую часть времени я стараюсь использовать «очень волшебный» режим, активируемый с помощью \v:

/\vabc(.*xyz)@!
person hansaplast    schedule 13.02.2019
comment
Так приятно узнать об этом, я был в аду VIM-Regex раньше - person polynomial_donut; 13.07.2020

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

Если вы ищете pattern2, которому не предшествует pattern1, то есть отрицательный просмотр назад, ищите:

\(pattern1\)\@<!pattern2
person hochl    schedule 22.02.2017
comment
Это эквивалентно (?!pattern1)pattern2 в PCRE. - person Mateen Ulhaq; 15.11.2018

Это работает для меня /abc\(.*xyz\)\@!

person priomsrb    schedule 15.01.2014

Думаю, я бы написал это как /abc\(\(xyz\)\@!.\)*$

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

"abc, за которым следует все, что не является xyz, любое количество раз до конца строки"

Мне это нравится больше, чем «abc, за которым следует что-то, за чем не следует xyz», как в других упомянутых шаблонах.

person Ben    schedule 16.01.2014