Is there a way to do negative lookahead in vim regex?

RegexVimNegative Lookahead

Regex Problem Overview


In Vim, is there a way to search for lines that match say abc but do not also contain xyz later on the line? So the following lines would match:

The abc is the best
The first three letters are abc

and the following would not match:

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

I know about syntax like the following:

/abc\(xyz\)\@!

but that only avoids matching abcxyz and not if there is anything in between, such as abc-xyz. Using

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

also does not work because there are many positions later in the line where xyz is not matched.

(I should note that on the command line I would do something like grep abc <infile | grep -v xyz but I would like to do the above interactively in Vim.)

Regex Solutions


Solution 1 - Regex

Your attempt was pretty close; you need to pull the .* that allows an arbitrary distance between the match and the asserted later non-match into the negative look-ahead:

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

I guess this works because the non-match is attempted for all possible matches of .*, and only when all branches have been exhausted is the \@! declared as fulfilled.

Solution 2 - Regex

I always find it weird, that you need to escape brackets in vim, so I try to use the "very magic" mode most of the time, activated with \v:

/\vabc(.*xyz)@!

Solution 3 - Regex

Although your question is about lookahead, I found it while searching for lookbehind. Thus, I post the solution to lookbehind so that others may find it.

If you search for pattern2 not preceded by pattern1, that is negative lookbehind, search for:

\(pattern1\)\@<!pattern2

Solution 4 - Regex

This works for me /abc\(.*xyz\)\@!

Solution 5 - Regex

I think I'd write this as /abc\(\(xyz\)\@!.\)*$

I'm not sure whether this is faster than other suggestions (I think it might be since it tests each position only once) but it makes conceptual sense as:

"abc followed by anything which is not xyz, any number of times until end of line"

I like that better than "abc followed by anything not followed by xyz" as in the other patterns mentioned.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionGreg HewgillView Question on Stackoverflow
Solution 1 - RegexIngo KarkatView Answer on Stackoverflow
Solution 2 - RegexhansaplastView Answer on Stackoverflow
Solution 3 - RegexhochlView Answer on Stackoverflow
Solution 4 - RegexpriomsrbView Answer on Stackoverflow
Solution 5 - RegexBenView Answer on Stackoverflow