Search, Replace & Regular Expressions

If there's one chapter in this book that will save you hours of manual editing, it's this one. The ability to search for patterns and transform text across an entire file — or even across multiple files — is what turns Vim from a text editor into a text processing engine.

This chapter brings together three tightly connected topics: searching, regular expressions, and substitution. We cover them in one place because in practice you'll use them together. You'll search for a pattern, refine it until it matches exactly what you want, and then use it in a substitution to transform your text.

What you'll learn in this chapter:

  • Write Vim regular expressions using both default and very magic (\v) syntax

  • Search forward and backward, and configure search settings like incremental and smart-case matching

  • Perform substitutions with flags, expression registers, and custom delimiters

  • Search and replace across multiple files using :vimgrep and the quickfix list

  • Apply practical regex patterns for tasks like reformatting dates, capitalizing text, and cleaning whitespace

Regular Expressions in Vim

Before diving into search and replace, let's establish a foundation with Vim's regex syntax. If you've used regular expressions in other languages, you'll feel mostly at home — but Vim has some quirks you should know about. The biggest difference is that many special characters require a backslash by default (unlike Perl/Python), which is why the \v (very magic) mode exists. Regex patterns pair especially well with text objects (see Text Objects) for selecting and operating on structured text.

Here's a reference of the most commonly used patterns:

Regex
What it matches

^

Start of a line

$

End of a line

.

Any single character

\s

Whitespace

\S

Non-whitespace

\d

Digit (same as [0-9])

\w

Word character

\W

Non-word character

[a-z]

Lowercase letter

\w\+

One or more word characters

\v

Very magic mode (avoid many backslashes)

\zs

Start of match (everything before is context)

\ze

End of match (everything after is context)

\n

Newline in search pattern

\r

Newline in replacement

".\{-}"

Non-greedy match between quotes

\@!

Negative lookahead

Very Magic Mode \v

By default, Vim requires many backslashes in regex. The \v (very magic) flag makes most special characters work without escaping:

Tip: Start your search patterns with \v to write cleaner regex.

Command
What it does

/pattern

Search forward

?pattern

Search backward

n

Next match

N

Previous match

*

Search word under cursor (forward)

#

Search word under cursor (backward)

Search Settings

Neovim: These are mostly default in Neovim. You can clear highlights with :nohlsearch or map it to a key.

Interactive Search Navigation

While typing a search pattern in the command line, you can use:

Key
Action

Ctrl+g

Jump to the next match while still typing

Ctrl+t

Jump to the previous match while still typing

This lets you preview and cycle through matches before pressing Enter to confirm.

Searching with Hexadecimal Notation

Search for non-breaking spaces or other special characters using hex:

Use ga to show the hex value of the character under the cursor.

Substitution Basics

The general form is:

Search and replace

Common Flags

Flag
Meaning

g

Replace all occurrences on each line (not just the first)

c

Ask for confirmation before each replacement

i

Case-insensitive for this substitution

I

Case-sensitive for this substitution

e

Don't show error if pattern not found

n

Count matches without replacing

Changing the Delimiter

You don't have to use /. Any character works:

Substitution Tricks

Using the Expression Register in Substitutions

The \= in the replacement part lets you evaluate Vimscript:

Using the Ampersand &

The & in the replacement represents the entire match:

Conditional Replacement

Use a ternary operator in the expression:

Conditional replacement

Converting Date Formats

Toggling Words with Regex

Exchange the last two words on each line:

Tip: The advantage of using regex groups () is that you can refer to them with \1, \2, etc.

To search across multiple lines, use \_ prefix:

Pattern Not Followed By Another Pattern

Use \@! for negative lookahead:

Searching Across Files

vimgrep

Quickfix Navigation

After vimgrep, results appear in the quickfix list:

Command
What it does

:copen

Open quickfix window

:ccl or :cclose

Close quickfix window

:cn

Jump to next entry

:cp

Jump to previous entry

:cdo {cmd}

Execute command on each quickfix entry

Quickfix window

Neovim: Telescope with live_grep and trouble.nvimarrow-up-right provide a much better experience for searching across files.

Regex Analysis: The DelBlank Function

The DelBlankLines function (see Chapter 5) uses three substitution commands. Here's a breakdown of each regex:

First: :%s/\s\+$//e — Remove trailing whitespace

Part
Meaning

\s

Space or tab

\+

One or more

$

At end of line

Second: :%s/^\n\{2,}/\r/ge — Squeeze consecutive blank lines into one

Part
Meaning

^

Start of line

\n

Line break

\{2,}

Two or more times

\r

Replace with single newline

Third: :%s/\v($\n\s*)+%$//e — Remove trailing blank lines at end of file

Part
Meaning

\v

Very magic mode

($\n\s*)

End of line + newline + optional spaces (one group)

+

One or more of this group

%$

End of file

Note: All three use the e flag to suppress "pattern not found" errors.

Practical Examples

Capitalize First Letter of Each Word in a Selection

The %V restricts the substitution to the visual selection.

Capitalize First Word After Punctuation

Part
Meaning

[.?!]

Period, question mark, or exclamation point

\_s*

Any whitespace including newlines

\zs

Start match here

\<\w\+\>

A whole word

\U&

Uppercase the match

Note: Notice the delimiter is ; to avoid conflicts with / in the pattern.

Remove Trailing Whitespace

Delete Blank Lines

Add a Blank Line After Each Non-Empty Line

Use Current Word in Commands

Press Ctrl+rCtrl+w in command mode to insert the word under the cursor:

Tip: The search and substitution patterns covered in this chapter become even more powerful when combined with the :global command (see The Global Command), which lets you apply commands selectively to lines matching a pattern.

Summary

Vim's search, substitution, and regex capabilities transform it from a text editor into a text processing engine. By combining forward and backward search, very magic regex syntax, and the flexible substitution command with its flags and expression register, you can perform text transformations that would otherwise require external scripts. Searching across files with :vimgrep and applying changes via the quickfix list extend these capabilities to entire projects.

Key takeaways:

  • Use \v (very magic) mode to write cleaner regular expressions that avoid excessive backslash escaping.

  • The substitution command supports powerful features like the expression register (\=), the ampersand (&) for the full match, and custom delimiters.

  • Combine :vimgrep with :cdo to search and replace across multiple files in a single workflow.

  • The \zs and \ze atoms let you define match boundaries within a larger pattern, giving you surgical precision.

Exercises

  1. Search with very magic mode — Open any file and search for all words that start with a capital letter followed by at least two lowercase letters. Use \v mode to keep the pattern clean.

  2. Substitute with confirmation — Replace all occurrences of foo with bar in the current file, but confirm each replacement before it happens. Skip any occurrences you want to keep.

  3. Swap two words using capture groups — Given a line like first_name last_name, write a substitution that swaps the two words so it becomes last_name first_name. Use very magic mode.

  4. Use the expression register in substitution — Given a file with lines like Price: 100, Price: 250, write a substitution that doubles every price number.

  5. Search and replace across files — Use :vimgrep to find all occurrences of old_function in every .py file in the current directory tree. Then replace them all with new_function using :cdo.

Last updated

Was this helpful?