Skip to content

Remplacer toutes les occurrences dans une chaîne snippet

Échanger chaque occurrence d'une sous-chaîne contre une autre.

Échanger chaque occurrence d'une sous-chaîne contre une autre. Les défauts sont d'une incohérence exaspérante entre langages : le replace() de JavaScript s'arrête au premier match (le bug le plus cloné d'Internet), tandis que Python, PHP, C#, Go, Rust, Swift, et même le replace() de Java parcourent toute la chaîne sauf si tu passes une limite. Préfère les APIs littérales quand l'aiguille vient de l'utilisateur — les moteurs regex interprètent ?, * et ( sauf si tu échappes (Pattern.quote, Regexp.escape, re.escape), et Java et JavaScript développent $1 dans le remplacement même pour des aiguilles littérales. SQL cache le piège le plus sournois : le replace() de Postgres est global, mais regexp_replace ne remplace que le premier match sauf si tu passes le flag 'g'.

Recette exécutable · 15 langages
Text & Parsingstringsreplacesubstringregextext

Every language

15 langages, copy-ready. One at a time with syntax highlighting, or all inline.

SQLSQLrunnable
-- PostgreSQL
SELECT replace('cat catalog concatenate', 'cat', 'dog'); -- dog dogalog condogenate — all occurrences

SELECT regexp_replace('a-b--c', '-', '+', 'g'); -- a+b++c — without 'g' you get a+b--c

Postgres dialect. replace() is literal and global. regexp_replace is the inverse trap: it fixes ONLY the first match unless you pass the 'g' flag — a silent half-done migration if you assume replace() semantics. UPDATE table SET col = replace(col, 'old', 'new') is the classic bulk-rename idiom.

Run in the SQL playground →
JSJavaScript
const out = 'cat catalog concatenate'.replaceAll('cat', 'dog');
console.log(out); // dog dogalog condogenate

replace('cat', 'dog') with a string needle silently fixes only the FIRST match — replaceAll (ES2021) or a /g regex is the fix. replaceAll throws on a regex without the g flag. The replacement is not verbatim either: $& and $1 are expanded even for literal needles — write $$ for a literal dollar.

TSTypeScript
function replaceAll(haystack: string, needle: string, replacement: string): string {
  return haystack.split(needle).join(replacement);
}

console.log(replaceAll('cat catalog concatenate', 'cat', 'dog')); // dog dogalog condogenate

split(needle).join(replacement) is the pre-ES2021 idiom that still downlevels to old targets: every occurrence, no regex semantics, no missing-g-flag trap. Edge case: an empty needle splits per character, so guard needle !== '' at the boundary if the input is untrusted.

GoGo
package main

import (
	"fmt"
	"strings"
)

func main() {
	out := strings.ReplaceAll("cat catalog concatenate", "cat", "dog")
	fmt.Println(out) // dog dogalog condogenate
}

strings.ReplaceAll is literal and unbounded; strings.Replace takes a fourth n argument when you want a cap (n = 1 is first-only). For patterns use regexp.MustCompile(...).ReplaceAllString, and wrap user-supplied needles in regexp.QuoteMeta so a stray '*' cannot blow up the compile.

RsRust
fn main() {
    let day = "2026-08-27".replace('-', '/');
    println!("{day}"); // 2026/08/27
}

str::replace replaces every occurrence and takes a &str or char pattern; replacen(pat, replacement, n) caps the count. The stdlib is literal-only — patterns need the regex crate, where Regex::replace_all uses ${1} (not $1) for capture groups in the replacement.

PHPPHP
<?php
$out = str_replace('cat', 'dog', 'cat catalog concatenate', $count);
echo $out, PHP_EOL;   // dog dogalog condogenate
echo $count, PHP_EOL;  // 3

str_replace is already global and literal — the reverse of JavaScript's trap — and its optional fourth argument reports the match count. str_ireplace is the case-insensitive sibling; arrays let you translate many needles in one pass. preg_replace is the pattern version and interpolates $1 and \1 in the replacement.

PyPython
out = "cat catalog concatenate".replace("cat", "dog")
print(out)  # dog dogalog condogenate
print("a-b-c".replace("-", "+", 1))  # a+b-c — count caps it

str.replace replaces ALL by default; the optional count argument is how you do less. It is literal, so no escaping worries — the regex sibling re.sub needs re.escape on user needles and uses backslash group refs in the replacement.

CC
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* Return a malloc'd copy of src with every occurrence of from replaced by to.
   Caller frees. Empty `from` returns a plain copy (searching for it would loop). */
char *replace_all(const char *src, const char *from, const char *to) {
    size_t from_len = strlen(from), to_len = strlen(to);
    if (from_len == 0) {
        char *copy = malloc(strlen(src) + 1);
        if (copy) strcpy(copy, src);
        return copy;
    }

    size_t count = 0;
    for (const char *p = strstr(src, from); p; p = strstr(p + from_len, from))
        count++;

    char *out = malloc(strlen(src) + 1 + count * to_len); /* safe upper bound */
    if (!out) return NULL;

    char *dst = out;
    const char *cur = src, *p;
    while ((p = strstr(cur, from)) != NULL) {
        memcpy(dst, cur, (size_t)(p - cur));
        dst += p - cur;
        memcpy(dst, to, to_len);
        dst += to_len;
        cur = p + from_len;
    }
    strcpy(dst, cur); /* trailing tail */
    return out;
}

int main(void) {
    char *out = replace_all("cat catalog concatenate", "cat", "dog");
    if (out) {
        puts(out); /* dog dogalog condogenate */
        free(out);
    }
    return 0;
}

No stdlib replace — the recipe is: count non-overlapping occurrences with strstr, malloc strlen + count * (to_len - from_len) (the +1 + count * to_len form avoids underflow when the replacement shrinks), then copy segment / replacement pairs and the tail. strstr is byte-based and skips nothing implicitly; empty `from` must be special-cased or strstr never advances.

C++C++
#include <iostream>
#include <string>

std::string replace_all(std::string s, const std::string& from, const std::string& to) {
    if (from.empty()) return s;
    size_t pos = 0;
    while ((pos = s.find(from, pos)) != std::string::npos) {
        s.replace(pos, from.size(), to);
        pos += to.size(); /* skip past the fresh text or self-overlaps loop forever */
    }
    return s;
}

int main() {
    std::cout << replace_all("cat catalog concatenate", "cat", "dog") << '\n'; // dog dogalog condogenate
}

std::string::replace swaps a [pos, len) range — it does not search, so it is always paired with find in a loop. Advancing pos past the inserted replacement is load-bearing: replacing "a" with "aa" would match its own output and spin forever. std::regex_replace is the pattern sibling and replaces all matches by default.

C#C#
string result = "cat catalog concatenate".Replace("cat", "dog");
Console.WriteLine(result); // dog dogalog condogenate

String.Replace replaces every occurrence and is literal — there is no first-only overload; reach for Regex.Replace (System.Text.RegularExpressions) for patterns, or IndexOf + Remove + Insert when you need exactly one swap. Regex.Replace is global by default; its count limit is an optional parameter, the opposite of JavaScript's default.

JvJava
public class ReplaceAllOccurrences {
    public static void main(String[] args) {
        String result = "cat catalog concatenate".replace("cat", "dog");
        System.out.println(result); // dog dogalog condogenate
    }
}

Three methods, three modes: replace(CharSequence, CharSequence) is ALL occurrences and literal (contrary to JS-fueled instinct); replaceAll(regex, repl) is ALL and regex; replaceFirst is one-and-done regex. In the regex variants, escape the needle with Pattern.quote and the replacement with Matcher.quoteReplacement — a raw $ or backslash in the replacement throws IllegalArgumentException.

SwSwift
let result = "cat catalog concatenate"
    .replacingOccurrences(of: "cat", with: "dog")
print(result) // dog dogalog condogenate

replacingOccurrences(of:with:) replaces every match and is literal by default — pass options: .regularExpression or .caseInsensitive to change the engine. It bridges to NSString, so the range-taking variant speaks UTF-16 offsets; convert with Range(nsRange, in: string) before touching Swift indices.

KtKotlin
fun main() {
    val result = "cat catalog concatenate".replace("cat", "dog")
    println(result) // dog dogalog condogenate
}

Kotlin's replace(CharSequence, CharSequence) replaces every literal occurrence — no first-only trap, no regex by accident. The Regex overloads (replace(regex, "-") or replace(regex) { match -> ... }) are the pattern siblings; there is no stdlib first-only replace, so a one-shot swap means indexOf plus substring stitching.

RbRuby
result = 'cat catalog concatenate'.gsub('cat', 'dog')
puts result # dog dogalog condogenate

sub replaces once; gsub ("global substitution") replaces everywhere — the mnemonic is the g. A String needle is literal, but escape user input with Regexp.escape before passing a Regexp. When the replacement itself contains backslashes or dollar signs, use the block form gsub('cat') { 'dog' } — block results are inserted verbatim with no reference expansion.

ZigZig
const std = @import("std");

pub fn main() !void {
    const input = "cat catalog concatenate";
    const out = try std.heap.page_allocator.alloc(
        u8,
        std.mem.replacementSize(u8, input, "cat", "dog"),
    );
    defer std.heap.page_allocator.free(out);

    const count = std.mem.replace(u8, input, "cat", "dog", out);
    std.debug.print("{s} — {d} replacements\n", .{ out, count }); // dog dogalog condogenate — 3 replacements
}

Two steps by design: replacementSize computes the exact output length (replacements may shrink or grow the buffer), then std.mem.replace writes into your slice and returns the match count. Everything is bytes — the stdlib has no regex, so pattern work needs a third-party package.

Keep going

Read the regex-tokens cheatsheet →