strings.CutSuffix prüft in einem Aufruf, ob ein String mit einem bestimmten Suffix endet, und schneidet dieses Suffix gleichzeitig ab. Die Funktion gibt zwei Werte zurück: before enthält den String ohne das Suffix (falls vorhanden) und found zeigt als boolesches Flag an, ob das Suffix tatsächlich entfernt wurde. Damit ersetzt sie das klassische Idiom if strings.HasSuffix(s, suf) { s = strings.TrimSuffix(s, suf); ... } durch einen einzigen Funktionsaufruf — übersichtlicher, weniger fehleranfällig und ohne doppelten Suffix-Vergleich.

Eingeführt wurde CutSuffix mit Go 1.20 als Spiegel zur ebenfalls neuen CutPrefix. Beide Funktionen schließen eine Lücke, die zuvor nur durch Kombination zweier Aufrufe geschlossen werden konnte, und passen ins bewährte Muster der Cut-Familie (strings.Cut), die seit Go 1.18 für beliebige Trenner zur Verfügung steht.

Die Signatur ist minimal und folgt der Konvention der Cut-Funktionen: zwei String-Parameter, zwei Rückgabewerte. Der zweite Rückgabewert vom Typ bool macht den Erfolg explizit und erlaubt die idiomatische Verwendung im if-Initial-Statement.

Go signatur.go
func CutSuffix(s, suffix string) (before string, found bool)

Die Funktion ist Teil des strings-Pakets der Standardbibliothek und benötigt keine externen Abhängigkeiten. Die Reihenfolge der Parameter — erst der zu untersuchende String, dann das Suffix — entspricht HasSuffix und TrimSuffix, sodass beim Umstieg keine Verwechslungsgefahr besteht.

Der erste Rückgabewert before enthält im Erfolgsfall den String ohne das abgeschnittene Suffix. Wird das Suffix nicht gefunden, gibt CutSuffix den unveränderten Original-String zurück — nicht etwa einen leeren String. Das ist wichtig, weil so Folgeoperationen ohne zusätzliche Prüfung mit dem Original arbeiten können.

Go rueckgabewerte.go
package main

import (
	"fmt"
	"strings"
)

func main() {
	// Erfolgsfall: Suffix vorhanden
	before, found := strings.CutSuffix("report.pdf", ".pdf")
	fmt.Printf("before=%q found=%v\n", before, found)

	// Misserfolgsfall: Suffix fehlt — Original bleibt erhalten
	before, found = strings.CutSuffix("report.pdf", ".docx")
	fmt.Printf("before=%q found=%v\n", before, found)
}
Output
before="report" found=true
before="report.pdf" found=false

Diese Semantik ist bewusst so gewählt: Der Aufrufer kann den Rückgabewert direkt weiterverwenden, ohne zwischen „gefunden" und „nicht gefunden" zu unterscheiden, falls beide Fälle denselben Folgepfad haben. Das found-Flag dient dann nur noch als optionale Information für Logging oder Verzweigungen.

Ein leeres Suffix "" ist per Definition in jedem String enthalten — auch im Leerstring selbst. CutSuffix(s, "") liefert daher konsistent (s, true), unabhängig vom Inhalt von s. Das verhält sich identisch zu strings.HasSuffix(s, ""), das ebenfalls true zurückgibt.

Go leeres_suffix.go
package main

import (
	"fmt"
	"strings"
)

func main() {
	before, found := strings.CutSuffix("hallo", "")
	fmt.Printf("before=%q found=%v\n", before, found)

	before, found = strings.CutSuffix("", "")
	fmt.Printf("before=%q found=%v\n", before, found)
}
Output
before="hallo" found=true
before="" found=true

Praktisch relevant ist dieses Verhalten vor allem, wenn das Suffix aus einer Variable kommt, die theoretisch leer sein kann — etwa aus Konfigurationsdaten oder Funktionsparametern. Eine separate Prüfung auf den Leerstring ist nicht nötig, weil das Ergebnis dann ohnehin der Original-String mit found=true ist.

Die drei Funktionen decken unterschiedliche Anwendungsfälle ab. Die folgende Tabelle zeigt, wann welche Variante die richtige Wahl ist:

FunktionRückgabeAnwendungsfall
HasSuffixboolNur prüfen, ohne den String zu verändern
TrimSuffixstringSuffix entfernen, ohne zu wissen ob es vorhanden war
CutSuffix(string, bool)Suffix entfernen UND wissen, ob es vorhanden war

TrimSuffix ist äquivalent zu CutSuffix, wenn man das found-Flag verwirft. HasSuffix allein reicht, wenn der String unverändert weiterverwendet wird. Die Kombination aus beidem deckt CutSuffix in einem Aufruf ab — das spart einen doppelten Suffix-Vergleich (intern arbeitet CutSuffix mit einem einzigen Byte-Vergleich) und macht den Code linearer lesbar.

Beim Verarbeiten von Dateilisten möchte man oft den Basisnamen ohne Endung gewinnen — etwa um daraus einen Slug oder eine Ausgabedatei mit anderer Endung zu bauen. CutSuffix macht beides in einem Schritt erkennbar: prüfen, ob die Datei den erwarteten Typ hat, und gleichzeitig den Basisnamen extrahieren.

Go dateiendung.go
package main

import (
	"fmt"
	"strings"
)

func main() {
	dateien := []string{"foto.jpg", "doku.pdf", "panorama.jpg", "notiz.txt"}

	for _, name := range dateien {
		if base, ok := strings.CutSuffix(name, ".jpg"); ok {
			fmt.Printf("Bild gefunden: %s -> Basis: %s\n", name, base)
		} else {
			fmt.Printf("Übersprungen:  %s\n", name)
		}
	}
}
Output
Bild gefunden: foto.jpg -> Basis: foto
Übersprungen:  doku.pdf
Bild gefunden: panorama.jpg -> Basis: panorama
Übersprungen:  notiz.txt

Das if-Initial-Statement if base, ok := strings.CutSuffix(...); ok ist hier besonders ausdrucksstark: Es kombiniert Bedingungsprüfung, Wertübernahme und Scope-Begrenzung in einer Zeile. Die Variable base existiert nur im if/else-Block, was Namenskollisionen mit der Schleifenvariable verhindert und die Absicht klar signalisiert.

URL-Pfade kommen in unterschiedlichen Formen an: mal mit, mal ohne abschließenden Slash. Für Vergleiche, Routing-Tabellen oder Cache-Keys ist eine kanonische Form nötig. CutSuffix liefert sie mit einem Aufruf — und der found-Wert kann gleichzeitig signalisieren, ob eine Weiterleitung nötig wäre.

Go trailing_slash.go
package main

import (
	"fmt"
	"strings"
)

// kanonisch liefert den Pfad ohne abschließenden Slash
// und meldet zurück, ob der Original-Pfad einen Slash hatte.
func kanonisch(pfad string) (string, bool) {
	if pfad == "/" {
		return "/", false // Root nicht antasten
	}
	return strings.CutSuffix(pfad, "/")
}

func main() {
	pfade := []string{"/blog/", "/blog", "/", "/docs/go/"}

	for _, p := range pfade {
		norm, hadSlash := kanonisch(p)
		fmt.Printf("%-12s -> %-10s (redirect=%v)\n", p, norm, hadSlash)
	}
}
Output
/blog/       -> /blog      (redirect=true)
/blog        -> /blog      (redirect=false)
/            -> /          (redirect=false)
/docs/go/    -> /docs/go   (redirect=true)

Der found-Wert wird hier semantisch umgedeutet: Er signalisiert, ob die Eingabe von der kanonischen Form abwich — also ob eine HTTP-301-Weiterleitung sinnvoll wäre. Diese Doppelnutzung des Flags ist ein typisches Muster bei CutSuffix und macht den Code kompakter als eine Variante mit getrenntem HasSuffix-Aufruf.

Seit Go 1.20 verfügbar

CutSuffix wurde in Go 1.20 (Februar 2023) eingeführt und steht in älteren Go-Versionen nicht zur Verfügung — go.mod muss mindestens go 1.20 deklarieren.

Spiegel zu CutPrefix

Gemeinsam mit strings.CutPrefix bildet sie das symmetrische Paar für Suffix- und Prefix-Operationen mit Erfolgs-Flag.

Kombiniert HasSuffix und TrimSuffix

Ein einziger Aufruf ersetzt das klassische Idiom aus HasSuffix-Prüfung und anschließendem TrimSuffix — der Suffix-Vergleich findet nur einmal statt.

Leeres Suffix liefert (s, true)

CutSuffix(s, "") gibt konsistent den unveränderten String und true zurück, analog zu HasSuffix(s, "").

Thread-sicher und seiteneffektfrei

Strings sind in Go unveränderlich; CutSuffix liest nur und kann von beliebig vielen Goroutinen parallel aufgerufen werden.

Allokationsfrei dank Slicing

Der zurückgegebene before-String ist intern ein Slice des Originals — es wird kein neuer Heap-Speicher reserviert.

Idiomatisch im if-Initial-Statement

Das Muster if before, ok := strings.CutSuffix(s, suf); ok { ... } begrenzt den Scope und macht die Absicht in einer Zeile sichtbar.

Case-sensitiv

Der Vergleich erfolgt byteweise; CutSuffix("Datei.JPG", ".jpg") liefert ("Datei.JPG", false) — vorher strings.ToLower anwenden, falls Groß-/Kleinschreibung ignoriert werden soll.

Weiterführende Ressourcen

Externe Quellen

/ Weiter

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

Zur Übersicht