Vim Regex: как искать A AND B NOT C
У меня есть много строк, содержащих имена президентов США Картера, Буша, Клинтона, Обамы. Некоторые из них содержат 1 из этих имен, некоторые 2, некоторые 3, некоторые из всех 4 (в любом порядке).
Я знаю, как искать Картера И Клинтона и Обаму →
:g/.*Carter\&.*Clinton\&.*Obama/p
Я знаю, как искать Картера И (Клинтон ИЛИ Буш) →
:g/.*Carter\&\(.*Clinton\|.*Bush\)/p
(Есть, безусловно, лучшие способы сделать это)
Но я не могу понять, как искать (и я рассматривал связанные вопросы), например, для Буша и Клинтона НЕ Картера и даже меньше того, как искать, например, для Буша и Клинтона НЕ (Carter OR Obama).
Ответы
Ответ 1
Чтобы представить NOT, используйте отрицательное утверждение \@!
.
Например, "NOT Bush" будет:
^\(.*Bush\)\@!
или используя \v
:
\v^(.*Bush)@!
Важно: обратите внимание на начало ^
. Хотя это необязательно, если вы используете только положительные утверждения (одно совпадение так же хорошо, как и любое другое), требуется привязать отрицательные утверждения (иначе они все равно могут совпадать в конце строки).
Перевод "Буш И Клинтон И НЕ (Картер ИЛИ Обама)":
\v^(.*Bush)&(.*Clinton)&(.*Carter|.*Obama)@!
Добавление
Объяснить связь между \&
и \@=
:
One&Two&Three
является взаимозаменяемым с:
(One)@=(Two)@=Three
Единственное отличие состоит в том, что \&
прямо отражает \|
(что должно быть более очевидным и естественным), а \@=
зеркала Perl (?=pattern)
.
Ответ 2
Если вы хотите использовать регулярные выражения в стиле Perl после vim, забудьте о \&
: это особенность, характерная для vim, которая бесполезна, поскольку vim также имеет lookaheads, поэтому любой r1\&r2
можно переписать как \%(r1\)\@=r2
. Но взгляды лучше, поскольку есть отрицательная версия, и они также доступны в большинстве движков регулярного выражения в стиле Perl. Ваш (Bush AND Clinton AND NOT (Carter OR Obama))
может быть выражен следующим образом:
g/^\%(.*\%(Carter\|Obama\)\)\@!\%(.*Bush\)\@=.*Clinton/
Или, с очень магии:
g/^\v%(.*%(Carter|Obama))@!%(.*Bush)@=.*Clinton/
См. :h /\@=
О внутренней логике: look-ahead - как ветки: для регулярного выражения (reg1)@=reg2
, предполагая, что reg2
соответствует в позиции N
(совпадение начинается с позиции N
), механизм regex проверяет, совпадает ли reg1
эта позиция. Если это не так, тогда позиция отбрасывается, и двигатель regex пытается выполнить следующее возможное совпадение для reg2
. То же самое для негативного внешнего вида, но с той разницей, что regex engine отбрасывает позицию, если соответствует reg1
.
Пример:
Regex: (.b)@!a
.
Строка: aba
.
- Найдено совпадение:
a
соответствует в позиции 0 (aba
). Попытка сопоставить прогноз: .
соответствует a
(aba
) и b
соответствует b
(aba
), совпадениям с опережением, отбрасыванию позиции.
- Позиция 1 (
aba
) не соответствует a
.
- Найдено совпадение:
a
соответствует позиции 2 (aba
). Попытка сопоставить прогноз "вперед": .
соответствует a
(aba
), но b
не соответствует: никаких символов не осталось, поиск вперед не выполняется. Результат: регулярное выражение соответствует позиции 2.