I have many lines containing the names of US Presidents Carter, Bush, Clinton, Obama. Some contain 1 of those names, some 2, some 3, some all 4 of them (in any order).
I know how to search for Carter AND Clinton AND Obama ->
:g/.*Carter\&.*Clinton\&.*Obama/p
I know how to search for Carter AND (Clinton OR Bush) ->
:g/.*Carter\&\(.*Clinton\|.*Bush\)/p
(There are most certainly better ways to do that)
But I can't figure how to search (and I looked at the related questions), e.g., for Bush AND Clinton NOT Carter and even less how to search, e.g., for Bush AND Clinton NOT (Carter OR Obama).
Vim has several regex modes, one of which is very magic that's very similar to traditional regex. Just put \v in the front and you won't have to escape as much.
[abc] : matches a, b, or c. [a-z] : matches every character between a and z (in Unicode code point order). [^abc] : matches anything except a, b, or c.
In normal mode, press / to start a search, then type the pattern ( \<i\> ), then press Enter. If you have an example of the word you want to find on screen, you do not need to enter a search pattern. Simply move the cursor anywhere within the word, then press * to search for the next occurrence of that whole word.
Example: The regex "aa\n" tries to match two consecutive "a"s at the end of a line, inclusive the newline character itself. Example: "a\+" matches "a+" and not a series of one or "a"s. ^ the caret is the anchor for the start of the string, or the negation symbol.
To represent a NOT, use the negative assertion \@!
.
For example, "NOT Bush" would be:
^\(.*Bush\)\@!
or using \v
:
\v^(.*Bush)@!
Important: note the leading ^
. While it's optional if you only use positive assertions (one match is as good as any other), it is required to anchor negative assertions (otherwise they can still match at the end of a line).
Translating "Bush AND Clinton AND NOT (Carter OR Obama)":
\v^(.*Bush)&(.*Clinton)&(.*Carter|.*Obama)@!
To explain the relationship between \&
and \@=
:
One&Two&Three
is interchangeable with:
(One)@=(Two)@=Three
The only difference is that \&
directly mirrors \|
(which should be more obvious and natural), while \@=
mirrors Perl's (?=pattern)
.
If you want to use Perl-style regular expressions after vim, forget about \&
: it is a vim-specific feature which is useless since vim also has lookaheads, so any r1\&r2
can be rewritten as as \%(r1\)\@=r2
. But lookaheads are better as there is a negative version of it and they are also available in most of Perl-style regular expression engines. Your (Bush AND Clinton AND NOT (Carter OR Obama))
can be expressed in the following way:
g/^\%(.*\%(Carter\|Obama\)\)\@!\%(.*Bush\)\@=.*Clinton/
Or, with very magic:
g/^\v%(.*%(Carter|Obama))@!%(.*Bush)@=.*Clinton/
See :h /\@=
About inner logic: look-ahead is like branches: for regex (reg1)@=reg2
assuming that reg2
matches at position N
(match starts at position N
), regex engine checks whether reg1
also matches at this position. If it does not, then the position is discarded and regex engine tries next possible match for reg2
. Same for the negative look-ahead, but with the difference that regex engine discards the position if reg1
does match.
Example:
Regex: (.b)@!a
.
String: aba
.
a
matches at position 0 (aba
). Trying to match look-ahead: .
matches a
(aba
) and b
matches b
(aba
), look-ahead matches, discarding position.aba
) does not match a
.a
matches at position 2 (aba
). Trying to match look-ahead: .
matches a
(aba
), but b
does not match: no symbols left, look-ahead fails. Result: regex matches at position 2.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With