strings.ReplaceAll ersetzt alle nicht-überlappenden Vorkommen eines Substrings old durch einen neuen Substring new und gibt das Ergebnis als frischen String zurück. Die Funktion wurde mit Go 1.12 eingeführt und ist nichts anderes als ein lesbarerer Shortcut für den Aufruf strings.Replace(s, old, new, -1) — die -1 als Limit hat in der Praxis fast jede Codebase irgendwann verwirrt, deshalb gibt es seit Go 1.12 diese explizite Variante.

In modernem Go-Code gilt ReplaceAll als idiomatische Wahl, wenn wirklich alle Treffer ersetzt werden sollen. Trotzdem lohnt ein Blick auf die Mechanik: ein leerer old-String hat eine spezielle Semantik, Ersetzungsketten können sich gegenseitig sabotieren, und für mehrere parallele Substitutionen ist strings.Replacer meist die bessere Wahl.

Die Signatur ist bewusst minimal: drei Strings rein, ein String raus. Es gibt keinen Fehlerrückgabewert und kein Limit, da die Semantik global gilt.

Go signatur.go
func ReplaceAll(s, old, new string) string

Alle Argumente sind string und damit immutable; das Ergebnis ist ein neu allozierter String, sobald mindestens ein Treffer vorhanden ist. Bei null Treffern darf die Implementierung den Eingabestring unverändert weiterreichen — das spart Allokationen.

Intern ruft ReplaceAll schlicht Replace mit n = -1 auf. Die -1 bedeutet bei Replace: kein Limit, alle Treffer ersetzen. Das folgende Beispiel zeigt die Identität beider Aufrufe — sowohl Ergebnis als auch Verhalten sind exakt gleich.

Go aequivalent.go
package main

import (
	"fmt"
	"strings"
)

func main() {
	s := "foo bar foo baz foo"

	a := strings.ReplaceAll(s, "foo", "QUX")
	b := strings.Replace(s, "foo", "QUX", -1)

	fmt.Println(a)
	fmt.Println(b)
	fmt.Println("gleich?", a == b)
}
Output
QUX bar QUX baz QUX
QUX bar QUX baz QUX
gleich? true

Funktional gibt es keinen Unterschied — der Vorteil liegt rein in der Lesbarkeit. Wer beim Lesen über -1 stolpert und kurz überlegen muss, was das bedeutet, profitiert von der expliziten ReplaceAll-Variante.

Ein leerer old-String ist ein Sonderfall, den viele Entwickler unterschätzen. ReplaceAll(s, "", new) fügt den new-String an jeder Position zwischen den Runen und zusätzlich am Anfang und Ende ein. Bei einem dreizeichigen String entstehen damit vier Einfügepunkte.

Go leeres_old.go
package main

import (
	"fmt"
	"strings"
)

func main() {
	s := "abc"
	fmt.Println(strings.ReplaceAll(s, "", "X"))

	// Auch bei UTF-8 wird zwischen Runen, nicht zwischen Bytes eingefügt
	u := "äöü"
	fmt.Println(strings.ReplaceAll(u, "", "-"))
}
Output
XaXbXcX
-ä-ö-ü-

Wichtig: die Einfügepunkte liegen zwischen Runen, nicht zwischen Bytes. Das macht die Funktion UTF-8-sicher und vermeidet kaputte Mehrbyte-Sequenzen. In der Praxis ist dieser Sonderfall selten gewollt — meistens entsteht er durch einen leer initialisierten Parameter, weshalb der old-Wert vor dem Aufruf geprüft werden sollte.

Eine klassische Falle entsteht, wenn mehrere ReplaceAll-Aufrufe nacheinander angewandt werden und der neue Wert eines Schritts zufällig dem alten Pattern eines späteren Schritts entspricht. Das Ergebnis ist dann nicht idempotent und führt zu schwer reproduzierbaren Bugs.

Go kreuz_replacement.go
package main

import (
	"fmt"
	"strings"
)

func main() {
	s := "a b"

	// Falsch: nacheinander ersetzen
	r1 := strings.ReplaceAll(s, "a", "b")
	r1 = strings.ReplaceAll(r1, "b", "a")
	fmt.Println("kette:  ", r1) // "a a" — beide wurden zu "a"!

	// Richtig: Replacer ersetzt parallel in einem Durchlauf
	rep := strings.NewReplacer("a", "b", "b", "a")
	fmt.Println("replacer:", rep.Replace(s)) // "b a"
}
Output
kette:   a a
replacer: b a

strings.Replacer löst dieses Problem, weil er in einem einzigen Scan-Durchlauf arbeitet und Ersetzungen nicht aufeinander anwendet. Faustregel: sobald zwei oder mehr ReplaceAll-Aufrufe in Reihe stehen und die Werte sich überlappen könnten, ist Replacer die robustere Wahl.

Die drei Werkzeuge decken unterschiedliche Bedürfnisse ab. Die folgende Tabelle fasst zusammen, wann welches Werkzeug die richtige Wahl ist.

WerkzeugZweckWann verwenden
strings.ReplaceAllAlle Treffer eines Patterns ersetzenStandardfall, ein Pattern, idiomatisch ab Go 1.12
strings.ReplaceMaximal n Treffer ersetzenNur die ersten n Vorkommen oder explizites -1
strings.NewReplacerMehrere Patterns parallel ersetzenMehrfach-Ersetzung, Vermeidung von Kreuz-Replacement, wiederverwendbar

Replacer ist außerdem vorteilhaft, wenn die gleichen Ersetzungsregeln viele Male auf unterschiedliche Strings angewandt werden — die interne Trie-Struktur wird einmal aufgebaut und dann wiederverwendet.

Wenn old im Eingabestring nicht vorkommt, gibt ReplaceAll den Originalstring zurück, ohne neuen Speicher zu allozieren. Sobald jedoch mindestens ein Treffer existiert, wird ein neuer String mit passender Kapazität gebaut — der Aufwand ist linear in der Länge des Inputs.

Für große Strings mit vielen Treffern ist ReplaceAll deutlich effizienter als eine handgeschriebene Schleife, weil intern strings.Builder zum Einsatz kommt. Bei Hot-Path-Code mit mehreren parallelen Substitutionen lohnt der Wechsel zu einem einmal vorbereiteten Replacer, da dieser zusätzlich die Pattern-Suche bündelt.

Mini-Templates mit {{var}}-Syntax lassen sich ohne externe Library bauen, solange die Anzahl der Variablen klein ist. Das folgende Beispiel zeigt eine simple Substitution für eine Mail-Vorlage, wie sie etwa für Benachrichtigungs-Templates praktikabel ist.

Go praxis_template.go
package main

import (
	"fmt"
	"strings"
)

func main() {
	tmpl := "Hallo {{name}}, dein Konto {{account}} ist aktiv."

	out := strings.ReplaceAll(tmpl, "{{name}}", "Anna")
	out = strings.ReplaceAll(out, "{{account}}", "acc-4711")

	fmt.Println(out)
}
Output
Hallo Anna, dein Konto acc-4711 ist aktiv.

Für mehrere Platzhalter ist strings.NewReplacer die effizientere und sicherere Variante — vor allem dann, wenn ein Variablenwert wieder einen {{...}}-Ausdruck enthalten könnte. Für ernsthafte Templates sollte ohnehin text/template zum Einsatz kommen.

Beim CSV-Schreiben verlangt RFC 4180, dass innerhalb gequoteter Felder ein doppeltes Anführungszeichen durch zwei Anführungszeichen escaped wird. Diese Transformation ist ein Lehrbuchfall für ReplaceAll.

Go praxis_csv.go
package main

import (
	"fmt"
	"strings"
)

func csvField(v string) string {
	escaped := strings.ReplaceAll(v, `"`, `""`)
	return `"` + escaped + `"`
}

func main() {
	fmt.Println(csvField(`Hallo "Welt"`))
	fmt.Println(csvField(`ohne Quote`))
	fmt.Println(csvField(`mehrere "" Quotes`))
}
Output
"Hallo ""Welt"""
"ohne Quote"
"mehrere """" Quotes"

In produktivem Code sollte natürlich encoding/csv aus der Standardbibliothek bevorzugt werden — diese Funktion kümmert sich um Quoting, Delimiter und Zeilenumbrüche zuverlässig. Das Beispiel zeigt aber, warum ReplaceAll für einfache Escape-Aufgaben so attraktiv ist: ein Aufruf, kein State, kein Allokationsoverhead bei Strings ohne Quote.

Shortcut für n=-1

strings.ReplaceAll(s, old, new) ist exakt identisch zu strings.Replace(s, old, new, -1) — keine versteckte Magie, nur lesbarer.

Seit Go 1.12 idiomatisch

Vor Go 1.12 war Replace(s, old, new, -1) Standard; seit 1.12 gilt ReplaceAll als bevorzugte Schreibweise für „alle Treffer ersetzen".

Leeres old fügt new überall ein

Mit old == "" landet new zwischen jeder Rune sowie am Anfang und Ende — fast nie das, was wirklich gewollt ist.

Kreuz-Replacement-Falle bei Ketten

Mehrere ReplaceAll-Aufrufe hintereinander können sich gegenseitig „kannibalisieren", wenn new-Werte dem old-Pattern eines späteren Schritts entsprechen.

Replacer für parallele Ersetzungen

strings.NewReplacer ersetzt mehrere Patterns in einem Scan-Durchlauf und vermeidet die Kreuz-Replacement-Falle vollständig.

Ohne Match = identischer String

Findet ReplaceAll keinen Treffer, wird der Originalstring ohne neue Allokation zurückgegeben — günstig im No-Op-Fall.

Byte-orientiert, aber UTF-8-sicher

Die Suche arbeitet auf Byte-Ebene, der Sonderfall old == "" orientiert sich aber an Rune-Grenzen — gültige UTF-8-Sequenzen bleiben intakt.

Threadsafe

Strings sind in Go immutable; ReplaceAll mutiert nichts und ist damit ohne weiteres Locking aus mehreren Goroutinen aufrufbar.

Weiterführende Ressourcen

Externe Quellen

/ Weiter

Zurück zu Das strings-Paket — String-Manipulation

Zur Übersicht