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.