string manipulation
substring replacement
programming tutorial
text processing
coding tips

How to replace multiple substrings of a string?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Replacing multiple substrings is a common text-processing task in ETL jobs, templating, sanitization, migration scripts, and parsing pipelines. The challenge is not writing one replace; it is choosing a strategy that is correct when patterns overlap, performant for large inputs, and maintainable as replacement rules evolve.

Depending on requirements, you can use:

  1. sequential replacements,
  2. regex with callback,
  3. single-pass tokenization/trie strategies for large rule sets.

Understanding tradeoffs prevents subtle bugs like accidental double replacement.

Core Sections

1. Sequential replacement (simple and readable)

For small rule sets, straightforward loops are often enough.

python
1def multi_replace_simple(text: str, mapping: dict[str, str]) -> str:
2    for old, new in mapping.items():
3        text = text.replace(old, new)
4    return text
5
6s = "cat and dog"
7print(multi_replace_simple(s, {"cat": "lion", "dog": "wolf"}))

This is easy to read, but order matters. Replacing "a" -> "b" then "b" -> "c" can cascade unexpectedly.

2. Regex-based replacement for controlled one-pass matching

Regex with alternation can apply one-pass replacement using original text spans.

python
1import re
2
3def multi_replace_regex(text: str, mapping: dict[str, str]) -> str:
4    pattern = re.compile("|".join(re.escape(k) for k in sorted(mapping, key=len, reverse=True)))
5    return pattern.sub(lambda m: mapping[m.group(0)], text)
6
7mapping = {
8    "New York": "NYC",
9    "York": "YRK"
10}
11
12print(multi_replace_regex("I like New York", mapping))
13# I like NYC

Sorting by descending key length helps with overlap where longest match should win.

3. Large-scale rules and performance considerations

For very large rule dictionaries (hundreds/thousands), regex alternation can become heavy. Alternatives:

  • Aho-Corasick automaton,
  • trie-based scanners,
  • specialized text-replacement libraries.

A simple rule-engine wrapper improves maintainability:

python
1from dataclasses import dataclass
2
3@dataclass(frozen=True)
4class ReplaceRule:
5    old: str
6    new: str
7
8class Replacer:
9    def __init__(self, rules: list[ReplaceRule]):
10        self.rules = rules
11
12    def apply(self, text: str) -> str:
13        for r in self.rules:
14            text = text.replace(r.old, r.new)
15        return text

This allows explicit ordering and test coverage per rule set.

4. Safety checks and testing

For production text transforms:

  • keep idempotency tests if pipeline runs more than once,
  • track before/after diff samples,
  • test overlap and boundary cases.

Example test cases:

python
1def test_overlap():
2    s = "abc"
3    m = {"ab": "X", "bc": "Y"}
4    # expected depends on selected strategy

Defining expected behavior for overlaps is critical; there is no universal default.

Common Pitfalls

  • Ignoring rule order in sequential replacement and getting cascading unintended substitutions.
  • Using regex alternation without escaping special characters in keys.
  • Not defining overlap policy (first-match, longest-match, deterministic order).
  • Replacing in large texts repeatedly with quadratic-like behavior due to poor algorithm choice.
  • Shipping replacement logic without tests for boundary and idempotency cases.

Summary

Multiple-substring replacement is easy to start and easy to get subtly wrong at scale. Use sequential replacement for small, controlled rule sets; use regex/callback for one-pass deterministic matching; and consider trie/Aho-Corasick for large rule volumes. Most importantly, define overlap semantics explicitly and test them.

For multilingual or case-insensitive transformations, plan rule design early. Simple direct replacement can fail when casing varies ("ERROR", "Error", "error") or when Unicode normalization differs between inputs. Regex with flags can help, but you should define whether replacements preserve original case, force canonical forms, or apply locale-aware behavior. Without explicit policy, teams often patch edge cases ad hoc, which eventually makes replacement logic brittle and hard to reason about.

It is also useful to log replacement counts in production text pipelines. If one rule suddenly spikes, it may indicate upstream formatting changes or malicious input patterns. Metrics such as per-rule hit count, average transformed length, and failure rate help detect regressions early. In security-sensitive contexts (for example redaction), add tests proving that sensitive tokens are replaced under all expected variants. Treat replacement rules as a managed asset with versioning and review, not just a helper function.


Course illustration
Course illustration

All Rights Reserved.