How to Find and Replace Text
Find-and-replace is one of the most fundamental text operations — but it hides surprising complexity once you move beyond simple string substitution. This guide covers plain-string replace, regex replace, case-insensitive matching, whole-word matching, backreferences, and bulk edits across files.
Plain String Replace vs Regex Replace
There are two modes for finding text:
| Mode | What it matches | Example pattern |
|---|---|---|
| Plain string | Exact characters, no special meaning | foo matches only foo |
| Regex | Pattern with wildcards, groups, quantifiers | f[oa]o matches foo and fao |
For simple word swaps (rename a function, fix a typo), plain string is safer and faster. Regex is necessary when the thing you want to replace varies in form — e.g., all email addresses, all hex colour codes, or all dates in MM/DD/YYYY format.
Case-Insensitive Replace
By default, most engines are case-sensitive: foo does not match Foo or FOO.
Enable case-insensitive matching with:
- JavaScript regex flag
i:/foo/gi - Python
re.IGNORECASEorre.I - Go
(?i)inline flag:(?i)foo - PHP
/foo/i
// Replace "colour" in any capitalisation with "color"
"Colour and colour and COLOUR".replace(/colour/gi, "color");
// "color and color and color"
Whole-Word Replace
Sometimes you want to replace cat but not concatenate or scat. Word-boundary anchors solve this:
\bcat\bmatchescatas a whole word\bis a zero-width assertion at the boundary between a word character (\w) and a non-word character
"cat concatenate scat category".replace(/\bcat\b/g, "dog");
// "dog concatenate scat category"
Without \b:
"cat concatenate scat".replace(/cat/g, "dog");
// "dog dogcatenate sdog" ← undesired
Replace All vs Replace First
A common mistake is replacing only the first match:
| Language | Replace first | Replace all |
|---|---|---|
| JavaScript | str.replace("old", "new") |
str.replaceAll("old", "new") or /old/g regex |
| Python | str.replace("old", "new", 1) |
str.replace("old", "new") (replaces all by default) |
| Go | strings.Replace(s, "old", "new", 1) |
strings.ReplaceAll(s, "old", "new") |
| PHP | str_replace("old", "new", $s) |
same — replaces all by default |
Python and PHP replace all occurrences by default. JavaScript's .replace() with a plain string replaces only the first — a frequent source of bugs.
Using Backreferences to Preserve Parts of the Match
Backreferences let you capture part of the match and reuse it in the replacement. This is the most powerful feature of regex replace.
Example: convert 2026-07-12 to 12/07/2026
"2026-07-12".replace(/(\d{4})-(\d{2})-(\d{2})/, "$3/$2/$1");
// "12/07/2026"
(\d{4})captures the year →$1(\d{2})captures the month →$2(\d{2})captures the day →$3
The replacement $3/$2/$1 reorders the captured groups.
Code Examples
JavaScript
const text = "The colour of the sky is colour blue.";
// Plain string — replace all (use replaceAll or /g regex)
const fixed = text.replaceAll("colour", "color");
// "The color of the sky is color blue."
// Case-insensitive regex
const fixedCI = text.replace(/colour/gi, "color");
// Whole-word replace
const wholeWord = "cat scat concatenate".replace(/\bcat\b/g, "dog");
// "dog scat concatenate"
// Backreference: wrap numbers in brackets
const wrapped = "Price is 42 dollars and 7 cents".replace(
/(\d+)/g,
"[$1]"
);
// "Price is [42] dollars and [7] cents"
// Named capture groups (ES2018+)
const dated = "2026-07-12".replace(
/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/,
"$<day>/$<month>/$<year>"
);
// "12/07/2026"
Python
import re
text = "The colour of the sky is colour blue."
# Plain string — replaces ALL occurrences by default
fixed = text.replace("colour", "color")
# "The color of the sky is color blue."
# Case-insensitive regex
fixed_ci = re.sub(r"colour", "color", text, flags=re.IGNORECASE)
# Whole-word replace
whole_word = re.sub(r"\bcat\b", "dog", "cat scat concatenate")
# "dog scat concatenate"
# Backreference: reorder date parts
dated = re.sub(r"(\d{4})-(\d{2})-(\d{2})", r"\3/\2/\1", "2026-07-12")
# "12/07/2026"
# Replacement using a function (dynamic replacement)
def double_number(m):
return str(int(m.group()) * 2)
result = re.sub(r"\d+", double_number, "I have 3 cats and 5 dogs")
# "I have 6 cats and 10 dogs"
Go
package main
import (
"fmt"
"regexp"
"strings"
)
func main() {
text := "The colour of the sky is colour blue."
// Plain string — replace all
fixed := strings.ReplaceAll(text, "colour", "color")
fmt.Println(fixed) // The color of the sky is color blue.
// Replace first only
firstOnly := strings.Replace(text, "colour", "color", 1)
fmt.Println(firstOnly) // The color of the sky is color blue.
// Case-insensitive regex
re := regexp.MustCompile(`(?i)colour`)
fixedCI := re.ReplaceAllString(text, "color")
fmt.Println(fixedCI)
// Whole-word replace
reWord := regexp.MustCompile(`\bcat\b`)
result := reWord.ReplaceAllString("cat scat concatenate", "dog")
fmt.Println(result) // dog scat concatenate
// Backreference: reorder date
reDateStr := regexp.MustCompile(`(\d{4})-(\d{2})-(\d{2})`)
dated := reDateStr.ReplaceAllString("2026-07-12", "$3/$2/$1")
fmt.Println(dated) // 12/07/2026
}
PHP
<?php
$text = "The colour of the sky is colour blue.";
// Plain string — replaces ALL by default
$fixed = str_replace("colour", "color", $text);
echo $fixed; // The color of the sky is color blue.
// Case-insensitive plain string
$fixedCI = str_ireplace("colour", "color", $text);
// Whole-word replace with regex
$wholeWord = preg_replace('/\bcat\b/', 'dog', 'cat scat concatenate');
echo $wholeWord; // dog scat concatenate
// Backreference: reorder date (use $1 or \\1)
$dated = preg_replace('/(\d{4})-(\d{2})-(\d{2})/', '$3/$2/$1', '2026-07-12');
echo $dated; // 12/07/2026
// Replace multiple search terms at once
$result = str_replace(
["colour", "behaviour", "neighbour"],
["color", "behavior", "neighbor"],
"colour and behaviour of my neighbour"
);
echo $result; // color and behavior of my neighbor
?>
Bulk Find-and-Replace Across Files
For replacing across many files, use command-line tools:
Linux / macOS (sed):
# Replace in-place (-i) across all .txt files recursively
find . -name "*.txt" -exec sed -i 's/colour/color/g' {} +
# With grep to preview first (no replace yet)
grep -r "colour" .
Windows PowerShell:
# Preview matches
Select-String -Path *.txt -Pattern "colour"
# Replace in-place
Get-ChildItem -Recurse -Filter "*.txt" | ForEach-Object {
(Get-Content $_.FullName) -replace "colour", "color" |
Set-Content $_.FullName
}
ripgrep + sed (fastest for large codebases):
# Find all files containing the pattern
rg -l "colour"
# Replace using ripgrep's list + sed
rg -l "colour" | xargs sed -i 's/colour/color/g'
Quick Reference
| Task | JavaScript | Python | Go | PHP |
|---|---|---|---|---|
| Replace all (plain) | .replaceAll(a, b) |
.replace(a, b) |
strings.ReplaceAll |
str_replace |
| Replace first | .replace(a, b) |
.replace(a, b, 1) |
strings.Replace(..., 1) |
preg_replace with limit |
| Case-insensitive | /pattern/gi |
re.sub(..., flags=re.I) |
(?i) inline flag |
/pattern/i |
| Whole-word | /\bword\b/g |
r"\bword\b" |
\bword\b |
/\bword\b/ |
| Backreference | $1, $2 (or named $<name>) |
\1, \2 |
$1, $2 |
$1, $2 (or \\1) |
| Dynamic replace | callback in .replace() |
function in re.sub() |
ReplaceAllStringFunc |
preg_replace_callback |
Frequently Asked Questions
Why does JavaScript's .replace() only replace the first occurrence?
It's a historical API design: the string overload replaces only the first match. Use .replaceAll() (ES2021+) or a regex with the g flag to replace all occurrences.
What's the difference between str_replace and preg_replace in PHP?str_replace is a plain-string replacement (fast, no regex overhead). preg_replace accepts a PCRE regular expression, giving you wildcards, quantifiers, and backreferences. Use str_replace for simple swaps; preg_replace when you need pattern matching.
How do I replace a literal dot or parenthesis with regex?
Escape the special character with a backslash: \. matches a literal dot, \( matches a literal opening parenthesis. In JavaScript strings you write "\\." to produce the regex \..
Can I undo a bulk find-and-replace?
Only if you have a backup or version control. Before running sed -i on many files, test with sed without -i first, or commit your changes to git so you can git checkout . if needed.
How do I replace text only within a certain line range?
With sed: sed '5,10s/old/new/g' file.txt applies the substitution only to lines 5 through 10. In Python, read the file into a list of lines, slice the range, apply .replace(), and rejoin.
What are named capture groups and when should I use them?
Named groups ((?<year>\d{4}) in JS/Python, (?P<year>\d{4}) in Python) let you reference captures by name instead of position ($<year> instead of $1). They make complex patterns much more readable and are worth using whenever a pattern has three or more groups.