Negative Look-behind


Negative Look-Behind patterns are look-around patterns that match only if the text immediately to the left of the match does not match the test-pattern. As with all look-around patterns, the "test pattern" is not part of the match.

This can be better explained with an example, again using our buffer containing a variety of dates:

Initial Conditions
2022-01-14
2022-07-09
2022-10-23
2022-12-20
2023-01-26
2023-03-17
2023-08-06
2023-11-05
COMMANDTop1:1
/\(2023-\)\@<!\(1\d\)

Suppose we want to match all months in Q4 (months 10, 11, and 12) that are not in 2023. The first step is to create the pattern that matches our desired content, the month portion of the date:

\(1d\)

Now that we have defined the main pattern, the next step is to define the test pattern. We only want to match months for which the year is not 2023, so we can use the test pattern:

\(2023-\)

Finally, to mark the test as a negative look-behind assertion we use \<! to get the final pattern:

\(2023-\)\@<!\(1\d\)"

when we execute this pattern:

Search with initial pattern
2022-01-14
2022-07-09
2022-10-23
2022-12-20
2023-01-26
2023-03-17
2023-08-06
2023-11-05
NORMALTop1:9
 

Er... not quite what we were hoping for. This result highlights one of the complications with using negative look-arounds - it is very easy to create patterns that don't match. In this case, our test pattern works fine when assuming that only the month will match our main pattern, but days that start with a "1" also match the main pattern, and the month never matches the test pattern when the day matches.

We can fix this by adding an alternate pattern to the test, so that it matches whenever the test is matched to a month.

\(\(2023-\)\|\(-\d\d-\)\)

Finally, with the modified test we get the result we were looking for:

Search with updated pattern
2022-01-14
2022-07-09
2022-10-23
2022-12-20
2023-01-26
2023-03-17
2023-08-06
2023-11-05
NORMAL33%3:6