strings.TrimPrefix entfernt einen ganzen Substring vom Anfang eines Strings — aber nur dann, wenn der String tatsächlich mit diesem Substring beginnt. Andernfalls bleibt der String unverändert zurück, ohne Fehler, ohne Allokation. Genau diese Eigenschaft macht die Funktion zum Standardwerkzeug, wenn ein optionales Präfix wie https://, ENV_ oder /api/v1 abgeschnitten werden soll.

Der kritische Unterschied zu strings.TrimLeft liegt in der Bedeutung des zweiten Arguments. TrimPrefix interpretiert das zweite Argument als kompletten Substring, der als Einheit passen muss. TrimLeft dagegen behandelt das zweite Argument als Menge einzelner Zeichen (Cutset) und schneidet von links jedes Zeichen weg, das in dieser Menge enthalten ist. Beide Funktionen liefern für dieselben Eingaben oft komplett verschiedene Ergebnisse — die Verwechslung ist eine der häufigsten Fallen im strings-Paket. TrimPrefix existiert seit den frühen Go-Versionen und ist seit Go 1.20 zusätzlich um die Variante CutPrefix ergänzt, die einen Erfolgs-Bool zurückgibt.

Die Signatur ist minimalistisch und drückt klar aus, was die Funktion tut: zwei Strings rein, ein String raus. Es gibt keinen Fehler-Return, kein Erfolgs-Flag — die Funktion ist total, jede Eingabe ist gültig.

Go signatur.go
func TrimPrefix(s, prefix string) string

Wenn s mit prefix beginnt, wird s[len(prefix):] zurückgegeben. Andernfalls wird s unverändert (identische Slice-Header) zurückgegeben. Beide Pfade sind allokationsfrei — die Funktion produziert keine neuen Strings im Heap, sondern arbeitet ausschließlich über Slicing der bestehenden String-Bytes.

Dieser Punkt ist die wichtigste Erkenntnis über TrimPrefix. Das zweite Argument wird als Ganzes mit dem Anfang verglichen, nicht als Zeichenmenge interpretiert. Wenn der String nicht exakt mit diesem Substring beginnt, geschieht nichts.

Go substring_vs_cutset.go
package main

import (
	"fmt"
	"strings"
)

func main() {
	s := "effective.go"

	// TrimPrefix: "file" als ganzer Substring → kein Match
	fmt.Printf("%q\n", strings.TrimPrefix(s, "file"))

	// TrimLeft: "file" als Cutset {f, i, l, e} → matcht 'e', 'f', 'f', 'e'
	fmt.Printf("%q\n", strings.TrimLeft(s, "file"))

	// TrimPrefix: passender Substring → entfernt
	fmt.Printf("%q\n", strings.TrimPrefix(s, "effective"))
}
Output
"effective.go"
"ctive.go"
".go"

Im ersten Fall liefert TrimPrefix den Originalstring zurück, weil "effective.go" nicht mit "file" beginnt. Im zweiten Fall behandelt TrimLeft "file" als Menge {f, i, l, e} und schneidet von links jedes Zeichen weg, das in dieser Menge liegt — also e, f, f, e. Die beiden Ergebnisse haben nichts miteinander zu tun, obwohl beide Aufrufe identisch aussehen.

TrimPrefix entfernt das Präfix genau einmal. Falls derselbe Substring direkt danach noch einmal auftaucht, bleibt er stehen. Wer wiederholtes Strippen braucht, muss die Funktion in einer Schleife aufrufen oder ein anderes Werkzeug wählen.

Go nur_einmal.go
package main

import (
	"fmt"
	"strings"
)

func main() {
	// "ab" wird nur einmal entfernt
	fmt.Printf("%q\n", strings.TrimPrefix("ababcd", "ab"))

	// Wiederholtes Strippen via Schleife
	s := "ababcd"
	for strings.HasPrefix(s, "ab") {
		s = strings.TrimPrefix(s, "ab")
	}
	fmt.Printf("%q\n", s)
}
Output
"abcd"
"cd"

Der erste Aufruf entfernt nur das erste "ab" und lässt das zweite stehen — das Ergebnis ist "abcd". Die Schleifenvariante wiederholt den Aufruf, solange HasPrefix true liefert, und entfernt damit alle führenden Wiederholungen. Diese Konstruktion ist allokationsfrei, weil jeder Schleifendurchlauf nur den Slice-Header verschiebt.

Ein leerer prefix ist ein gültiger Aufruf und führt zu einem No-Op: der String wird unverändert zurückgegeben. Das matcht die mathematische Definition — jeder String beginnt mit dem leeren Substring, aber das Abschneiden von null Bytes ändert nichts.

Go leeres_praefix.go
package main

import (
	"fmt"
	"strings"
)

func main() {
	fmt.Printf("%q\n", strings.TrimPrefix("hallo", ""))
	fmt.Printf("%q\n", strings.TrimPrefix("", ""))
	fmt.Printf("%q\n", strings.TrimPrefix("", "abc"))
}
Output
"hallo"
""
""

Auch der leere Eingabestring ist unproblematisch — er beginnt mit jedem leeren Präfix (Ergebnis: leer) und mit keinem nicht-leeren Präfix (Ergebnis: ebenfalls leer, unverändert). Es gibt keine Panics, keine Sonderfälle, die Funktion ist auf allen Eingaben definiert.

Diese drei Funktionen lösen verwandte Probleme, unterscheiden sich aber in dem, was sie zurückgeben. Die Wahl hängt davon ab, ob nur das gestrippte Ergebnis, eine Erfolgsmeldung oder beides gebraucht wird.

FunktionRückgabeWann nutzen
TrimPrefix(s, p)string (entfernt oder unverändert)wenn der Erfolg egal ist, nur das Ergebnis zählt
CutPrefix(s, p)(after string, found bool)wenn man wissen muss, ob das Präfix da war
HasPrefix(s, p)bool (nur Prüfung)wenn nur die Prüfung gebraucht wird, ohne Trim

CutPrefix ist seit Go 1.20 verfügbar und ersetzt das alte Idiom if HasPrefix { TrimPrefix } durch einen einzigen, allokationsfreien Aufruf. Wer nicht auf Go 1.20+ angewiesen ist oder die Erfolgsinformation nicht braucht, kann weiterhin bequem TrimPrefix verwenden — beide Funktionen sind so eng verwandt, dass der Wechsel trivial bleibt.

TrimPrefix ist eine der billigsten Funktionen im strings-Paket. Im Match-Fall wird der Bytevergleich byteweise bis zur Länge len(prefix) durchgeführt und dann ein neuer Slice-Header s[len(prefix):] über demselben Backing-Array zurückgegeben — keine Kopie, keine Heap-Allokation, nur Pointer-Arithmetik. Im Kein-Match-Fall wird sofort der Originalstring zurückgegeben, ebenfalls ohne Allokation.

In Hot-Paths wie URL-Routing, Header-Parsing oder Schlüsselnormalisierung ist TrimPrefix damit praktisch kostenlos. Wer trotzdem benchmarkt, sollte beachten, dass der Compiler den Funktionsaufruf in vielen Fällen inlinen kann — der reale Overhead landet oft bei Null Nanosekunden pro Aufruf.

Ein typisches Anwendungsfeld ist das Aufbereiten von URLs für die Anzeige. Nutzer wollen in einer Liste meist nicht https://example.com/seite sehen, sondern nur die Domain mit Pfad. TrimPrefix ist hier ideal, weil es beide Fälle (mit und ohne Schema) gleich behandelt.

Go praxis_url.go
package main

import (
	"fmt"
	"strings"
)

func displayURL(u string) string {
	u = strings.TrimPrefix(u, "https://")
	u = strings.TrimPrefix(u, "http://")
	u = strings.TrimPrefix(u, "www.")
	return u
}

func main() {
	urls := []string{
		"https://example.com/start",
		"http://example.com/start",
		"www.example.com",
		"example.com",
	}
	for _, u := range urls {
		fmt.Printf("%-30s -> %s\n", u, displayURL(u))
	}
}
Output
https://example.com/start      -> example.com/start
http://example.com/start       -> example.com/start
www.example.com                -> example.com
example.com                    -> example.com

Die Funktion arbeitet sich durch eine Kette von möglichen Präfixen. Jeder Aufruf ist ein No-Op, falls das Präfix fehlt — es gibt also keinen Grund, vorher mit HasPrefix zu prüfen. Der letzte Fall (example.com ohne jedes Präfix) durchläuft alle drei TrimPrefix-Aufrufe und bleibt komplett unverändert.

Beim Einlesen von Umgebungsvariablen oder Konfigurationsschlüsseln ist oft ein gemeinsames Präfix wie MIBEON_, APP_ oder ENV_ vorhanden, das vor der weiteren Verarbeitung verschwinden soll. TrimPrefix macht das in einer Zeile, sauber und ohne Sonderfall für fehlende Präfixe.

Go praxis_config.go
package main

import (
	"fmt"
	"strings"
)

func normalizeKey(key string) string {
	key = strings.TrimPrefix(key, "ENV_")
	return strings.ToLower(key)
}

func main() {
	keys := []string{
		"ENV_DATABASE_URL",
		"ENV_PORT",
		"DEBUG", // kein ENV_-Präfix
	}
	for _, k := range keys {
		fmt.Printf("%-20s -> %s\n", k, normalizeKey(k))
	}
}
Output
ENV_DATABASE_URL     -> database_url
ENV_PORT             -> port
DEBUG                -> debug

Der Schlüssel DEBUG hat kein ENV_-Präfix und bleibt deshalb beim TrimPrefix-Aufruf unverändert. Anschließend wird er ganz normal kleingeschrieben. Genau dieses Verhalten — fehlendes Präfix ist kein Fehler, sondern ein No-Op — macht TrimPrefix so angenehm in Pipelines, in denen die Eingabeformate gemischt sein können.

Substring, kein Cutset

TrimPrefix vergleicht den zweiten Parameter als zusammenhängenden Substring mit dem Anfang von s. Nur ein kompletter Match löst das Entfernen aus.

Häufigste Verwechslung: TrimLeft

TrimLeft interpretiert sein zweites Argument als Menge einzelner Zeichen. Identisch aussehende Aufrufe liefern bei TrimPrefix und TrimLeft oft komplett unterschiedliche Ergebnisse.

Nur einmal entfernt

Auch wenn das Präfix mehrfach hintereinander auftaucht, wird nur das erste Vorkommen abgeschnitten. Wiederholtes Strippen braucht eine Schleife mit HasPrefix.

Leeres Präfix = No-Op

TrimPrefix(s, "") gibt s unverändert zurück, ohne Panic oder Sonderfall — konsistent mit der Definition, dass jeder String mit dem leeren Präfix beginnt.

Ohne Match = Original zurück

Wenn s nicht mit prefix beginnt, ist der Rückgabewert byte-identisch mit s — keine Kopie, kein Fehler. Vorprüfung mit HasPrefix ist daher unnötig.

CutPrefix ab Go 1.20

CutPrefix liefert zusätzlich ein found bool und ersetzt das alte if HasPrefix { TrimPrefix }-Idiom durch einen einzigen Aufruf.

Allokationsfrei

Beide Pfade — Match wie Kein-Match — kommen ohne Heap-Allokation aus. Die Funktion verschiebt nur den Slice-Header über demselben Backing-Array.

Threadsafe

Strings in Go sind immutable, deshalb ist TrimPrefix ohne weitere Synchronisation aus mehreren Goroutines parallel aufrufbar.

Weiterführende Ressourcen

Externe Quellen

/ Weiter

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

Zur Übersicht