All posts

Find and Replace with Regular Expressions: A Practical Guide

July 27, 2026 · DevTools

regex
find-and-replace
text-editing
developer-tools

You need to rename 300 API endpoints from /v1/getUser to /v2/getUser. A manual edit would take all day and introduce typos. A plain-text find-and-replace catches all 300 at once — but /v1/ appears in many places you do not want to change (comments, docs, test fixtures). What you actually need is to replace /v1/ only when it is followed by a known set of route names.

That is where regex find-and-replace earns its place. The same tool that lets you search precisely lets you replace precisely — with capture groups, backreferences, and conditional patterns that no plain-text search can match.

The Find and Replace Text tool supports both literal and regex modes. Here is what regex mode unlocks.

Literal vs. regex mode: the critical difference

In literal mode, the search string is taken literally: $ searches for the dollar sign character. In regex mode, $ is an anchor matching the end of a line. If you type price: $50 in regex mode expecting to match the literal string, you will get unexpected results — or an error.

The Find and Replace Text defaults to literal mode. Always confirm which mode you are in before running a replace, especially when your search string contains characters that are regex metacharacters: ., *, +, ?, [, ], (, ), {, }, ^, $, |, \.

Capture groups and backreferences

Parentheses in a regex define a capture group. In the replacement string, $1, $2, $3 refer to the captured text. This is the most useful feature for batch renaming.

Rename endpoints:

Search:  /v1/(getUser|createUser|deleteUser)
Replace: /v2/$1

/v1/getUser becomes /v2/getUser. createUser and deleteUser are renamed too. Without capture groups, you would need three separate searches.

Swap two words:

Search:  (\w+), (\w+)
Replace: $2 $1

Smith, John becomes John Smith. The comma and space are matched but not captured — they are preserved in the output.

The dot, greediness, and lazy matching

. matches any character except newline. .* matches any sequence of characters. By default, .* is greedy — it matches as much as possible. Given the string <b>bold</b> and <b>more</b>:

Search:  <b>.*</b>
Replace: [BOLD]

Greedy .* matches from the first <b> all the way to the last </b> — producing [BOLD] instead of two separate matches. The fix is lazy matching: .*? matches as few characters as possible.

Search:  <b>.*?</b>
Replace: [BOLD]

Now it matches <b>bold</b> and <b>more</b> separately.

Anchors: start and end of line

^ matches the start of a line. $ matches the end.

# Add a prefix to every line that starts with "ERROR"
Search:  ^(ERROR.*)
Replace: [ALERT] $1
# Trim trailing whitespace from every line
Search:  \s+$
Replace: (empty)

Alternation for multiple patterns

| means "or". Use it to catch multiple variants:

Search:  (deprecated|obsolete|will-be-removed-in-v\d+)
Replace: [LEGACY: $1]

This catches three different deprecation styles and tags them uniformly.

Negative lookahead and lookbehind

These let you match a pattern only when it is not followed by (or preceded by) something. They do not consume characters — they assert a condition.

Replace foo only when not followed by Bar:

Search:  foo(?!Bar)
Replace: replacement

Replace foo only when preceded by baz:

Search:  (?<=baz)foo
Replace: replacement

Lookarounds are supported by most modern regex engines including JavaScript's.

Practical example: API migration

Your team is migrating from /api/user?id=123 query-param style to /api/users/123 path style.

Search:  /api/user\?id=(\d+)
Replace: /api/users/$1

The ? is escaped (\?) because ? is a regex metacharacter meaning "zero or one of the preceding." \? matches the literal question mark in the URL. \d+ captures one or more digits, which becomes $1 in the replacement.

/api/user?id=42    → /api/users/42
/api/user?id=1001  → /api/users/1001
/api/user?id=0     → /api/users/0

Test your pattern with the RegExp Tester before running a replace on the full file — a bad regex in replace mode edits the wrong text across every occurrence.

The $$ escape in replacements

In the replacement string, $ introduces a backreference. To insert a literal $ in the output, use $$. This comes up when adding currencies:

Search:  price: \d+\.\d{2}
Replace: price: $$&

$& in the replacement string inserts the entire matched text. $$ outputs a literal dollar sign.

Use the Find and Replace Text for batch edits across code, config files, or data. Always preview matches before replacing, and test your pattern on a small sample first.