Skip to main content

How to Perform Case-Insensitive String Replace in Python

Python's built-in .replace() method is strictly case-sensitive. To replace "Apple", "apple", and "APPLE" with the same replacement, you need to use regular expressions.

Using re.sub() with IGNORECASE

The re module provides the re.IGNORECASE flag (or its shorthand re.I) for case-insensitive matching:

import re

text = "New York, new york, NEW YORK are all the same city."

# Replace all case variations with "NY"
result = re.sub(r"new york", "NY", text, flags=re.IGNORECASE)

print(result)

Output:

NY, NY, NY are all the same city.

Using the Inline Flag

You can embed the flag directly in the pattern using (?i):

import re

text = "Apple, apple, APPLE - all fruits."

result = re.sub(r"(?i)apple", "orange", text)

print(result)

Output:

orange, orange, orange - all fruits.

Handling Special Characters

If your search term contains regex special characters (like ., *, +, $), use re.escape() to treat them as literal text:

import re


def replace_insensitive(text: str, old: str, new: str) -> str:
"""Replace all occurrences of 'old' with 'new' (case-insensitive)."""
pattern = re.compile(re.escape(old), re.IGNORECASE)
return pattern.sub(new, text)


# Safe with special characters
text = "Learn C++ and c++ programming"
result = replace_insensitive(text, "C++", "Python")

print(result)

Output:

Learn Python and Python programming
Without re.escape()

Special regex characters will be interpreted as patterns:

  • . matches any character
  • + means "one or more"
  • $ matches end of string

Always use re.escape() when replacing user-provided or variable strings.

Compiled Regex for Performance

When performing multiple replacements in a loop, compile the regex pattern once for better performance:

import re


def filter_comments(comments: list, bad_words: list) -> list:
"""Replace bad words with asterisks (case-insensitive)."""
# Compile patterns once
patterns = [
re.compile(re.escape(word), re.IGNORECASE)
for word in bad_words
]

filtered = []
for comment in comments:
for pattern in patterns:
comment = pattern.sub("****", comment)
filtered.append(comment)

return filtered


bad_words = ["spam", "scam", "fake"]
comments = [
"This is SPAM content",
"Total Scam alert!",
"Great product, not fake",
"Lovely genuine post"
]

for comment in filter_comments(comments, bad_words):
print(comment)

Output:

This is **** content
Total **** alert!
Great product, not ****
Lovely genuine post

Replacing Multiple Patterns at Once

Combine multiple words into a single regex pattern for efficiency:

import re


def multi_replace_insensitive(text: str, replacements: dict) -> str:
"""Replace multiple words case-insensitively."""
# Create pattern matching any of the keys
pattern = re.compile(
"|".join(re.escape(key) for key in replacements.keys()),
re.IGNORECASE
)

def replace_match(match):
# Look up replacement using casefolded key
word = match.group(0)
for key, value in replacements.items():
if key.casefold() == word.casefold():
return value
return word

return pattern.sub(replace_match, text)


text = "Visit New York or Los Angeles this summer."
replacements = {
"New York": "NYC",
"Los Angeles": "LA"
}

result = multi_replace_insensitive(text, replacements)
print(result)

Output:

Visit NYC or LA this summer.

Preserving Original Case

Sometimes you want to replace text while preserving the original's case pattern:

import re


def replace_preserve_case(text: str, old: str, new: str) -> str:
"""Replace while attempting to preserve case pattern."""

def match_case(match):
original = match.group(0)

if original.isupper():
return new.upper()
elif original.islower():
return new.lower()
elif original.istitle():
return new.title()
else:
return new

pattern = re.compile(re.escape(old), re.IGNORECASE)
return pattern.sub(match_case, text)


text = "The APPLE fell from the apple tree. Apple pie is great."
result = replace_preserve_case(text, "apple", "orange")

print(result)

Output:

The ORANGE fell from the orange tree. Orange pie is great.

Limiting Replacements

Control how many replacements occur using the count parameter:

import re

text = "cat CAT Cat cAt CaT"

# Replace only first 2 occurrences
result = re.sub(r"cat", "dog", text, count=2, flags=re.IGNORECASE)

print(result)

Output:

dog dog Cat cAt CaT

Method Comparison

MethodUse CaseHandles Special Chars
str.replace()Exact case match onlyYes (literal)
re.sub()Case-insensitiveNeeds re.escape()
Compiled regexMultiple replacementsNeeds re.escape()

Complete Utility Function

A production-ready function combining all best practices:

import re
from functools import lru_cache


@lru_cache(maxsize=128)
def _get_pattern(text: str) -> re.Pattern:
"""Cache compiled patterns for repeated use."""
return re.compile(re.escape(text), re.IGNORECASE)


def replace_ignore_case(
text: str,
old: str,
new: str,
count: int = 0
) -> str:
"""
Replace occurrences of 'old' with 'new' (case-insensitive).

Args:
text: The input string
old: The substring to find
new: The replacement string
count: Maximum replacements (0 = unlimited)

Returns:
The modified string
"""
if not old:
return text

pattern = _get_pattern(old)
return pattern.sub(new, text, count=count)


# Usage
print(replace_ignore_case("Hello WORLD world", "world", "Python"))

Output:

Hello Python Python

Summary

  • Use re.sub() with re.IGNORECASE for case-insensitive replacement.
  • Use re.escape() to safely handle special regex characters.
  • Compile patterns when performing repeated replacements for better performance.
  • Consider preserving case when replacing text in user-facing content.