How do I use \zs and \ze to define the exact boundaries of a regex match in Vim?
Answer
\zs and \ze in a pattern
Explanation
Vim's \zs ("match start") and \ze ("match end") atoms let you narrow the actual match region within a broader pattern context. Everything before \zs must be present but is excluded from the match, and everything after \ze must be present but is also excluded. This makes them the Vim equivalent of look-around assertions, and they work in search patterns, :substitute, :global, and anywhere else a Vim regex is accepted.
How it works
\zs— sets the start of the match at this position; text before it is required context but not captured\ze— sets the end of the match at this position; text after it is required context but not captured- Both atoms are zero-width: they consume no characters themselves
- When omitted, the match spans the entire pattern as usual
Example
Replace every function name in calls like call foo(...) without touching the word call:
:%s/call \zs\w\+/renamed_fn/g
Before:
call foo(x)
call foo(y)
After:
call renamed_fn(x)
call renamed_fn(y)
Or highlight only the value inside double-quoted strings:
/"\zs[^"]*\ze"
This positions the cursor on the value, not the opening quote.
Tips
\zsand\zecan both appear in the same pattern; only the region between them is highlighted or substituted- Useful for renaming identifiers that share a prefix:
/class \zsMyClass\ze/matches onlyMyClasswhile requiringclassbefore it - Combine with
\<and\>word boundaries for precise whole-word matches with context - These are Vim-specific; POSIX
grepand PCRE lookaheads ((?<=...)/(?=...)) use different syntax