strings.Trim entfernt vom Anfang und Ende eines Strings solange Zeichen, wie diese im übergebenen Cutset enthalten sind. Sobald an einer Seite ein Zeichen auftaucht, das nicht im Cutset steckt, hält das Trimmen für diese Seite an. Die Funktion arbeitet vollständig rune-aware: Der Cutset wird als Folge von Unicode-Codepoints interpretiert, nicht als Byte-Folge, weshalb auch Umlaute, kyrillische Zeichen oder Emojis problemlos als Trim-Zeichen funktionieren.
Die mit Abstand häufigste Falle liegt im Verständnis des Parameters: Der Cutset ist eine Menge einzelner Runes, kein Substring. Wer Trim(s, "file") schreibt, entfernt nicht das Wort file an den Rändern, sondern jedes einzelne Vorkommen der Zeichen f, i, l und e. Für das Abschneiden ganzer Substrings ist TrimPrefix bzw. TrimSuffix zuständig — eine Unterscheidung, die in Reviews regelmässig zu Bugs führt.
Die Signatur ist denkbar schlank: zwei Strings rein, ein String raus. Der zweite Parameter heisst zwar cutset string, wird intern aber sofort in einen Rune-Set zerlegt — der String ist nur die kompakteste Schreibweise einer Menge von Codepoints.
func Trim(s, cutset string) stringDer Rückgabewert ist entweder der getrimmte Teil-String (als neuer Header, aber auf dem gleichen Backing-Array) oder — wenn nichts zu trimmen war — s selbst unverändert. Eine Allokation findet nur statt, wenn tatsächlich Zeichen abgeschnitten werden.
Der Cutset-String wird zeichenweise als Set interpretiert. Reihenfolge und Duplikate spielen keine Rolle: "abc", "cba" und "aabbcc" ergeben denselben Set {a, b, c}. Trim läuft dann von links und rechts solange weiter, bis jeweils ein Zeichen ausserhalb dieses Sets erreicht wird.
package main
import (
"fmt"
"strings"
)
func main() {
// Cutset = Menge {f, i, l, e} - jedes dieser Zeichen wird entfernt
fmt.Printf("%q\n", strings.Trim("effective.go", "file"))
// Cutset = Menge {., g, o}
fmt.Printf("%q\n", strings.Trim("effective.go", ".go"))
// Wer wirklich das Wort "file" entfernen will, braucht TrimPrefix
fmt.Printf("%q\n", strings.TrimPrefix("file_handler.go", "file"))
}"ctiv.go"
"effective"
"_handler.go"Im ersten Beispiel wird links eff und rechts e.go (genauer: e, ., g, o von rechts) abgeschnitten, bis links das c und rechts das v stehen — beides nicht im Cutset. Das zweite Beispiel zeigt, wie sich Reihenfolge im Cutset nicht auswirkt: .go ist nur Notation für die Menge {., g, o}.
Go-Strings sind UTF-8-codiert, und Trim dekodiert den Cutset korrekt in Runes. Multi-Byte-Codepoints lassen sich daher genauso natürlich als Trim-Zeichen verwenden wie ASCII-Zeichen — ohne dass man sich um die Byte-Länge kümmern muss.
package main
import (
"fmt"
"strings"
)
func main() {
// Umlaute im Cutset
fmt.Printf("%q\n", strings.Trim("äöüHalloüöä", "äöü"))
// Emoji im Cutset
fmt.Printf("%q\n", strings.Trim("🌟🌟Mibeon🌟", "🌟"))
// Gemischt: ASCII + Multi-Byte
fmt.Printf("%q\n", strings.Trim("--→Pfad→--", "-→"))
}"Hallo"
"Mibeon"
"Pfad"Würde Trim byteweise arbeiten, würden Umlaute fälschlich an einzelnen Bytes gespalten. Die Rune-aware-Implementierung garantiert, dass ä (zwei Bytes in UTF-8) als ein einziges Trim-Zeichen behandelt wird.
Ist der Cutset ein leerer String, ist die Menge leer — und kein Zeichen kann gematcht werden. Trim gibt dann den Original-String unverändert zurück, ohne Allokation und ohne Whitespace-Sonderbehandlung.
package main
import (
"fmt"
"strings"
)
func main() {
s := " hallo "
out := strings.Trim(s, "")
fmt.Printf("%q\n", out)
fmt.Println(out == s) // identisch, keine Veränderung
}" hallo "
trueWer Whitespace trimmen will, braucht TrimSpace — das nutzt intern einen festen Cutset gemäss unicode.IsSpace und behandelt damit auch exotische Whitespace-Codepoints wie U+00A0 (NBSP) korrekt.
Die Trim-Familie wirkt auf den ersten Blick redundant, jede Variante hat aber einen klar abgegrenzten Anwendungsfall. Die folgende Tabelle hilft, die richtige Funktion ohne Nachdenken zu wählen.
| Funktion | Was entfernt wird | Wo | Argument-Typ |
|---|---|---|---|
Trim(s, cutset) | jedes Zeichen aus Cutset | links + rechts | Rune-Set |
TrimLeft(s, cutset) | jedes Zeichen aus Cutset | nur links | Rune-Set |
TrimRight(s, cutset) | jedes Zeichen aus Cutset | nur rechts | Rune-Set |
TrimSpace(s) | alle Unicode-Whitespaces | links + rechts | fix (intern) |
TrimPrefix(s, prefix) | genau dieser Substring | nur links, einmalig | Substring |
TrimSuffix(s, suffix) | genau dieser Substring | nur rechts, einmalig | Substring |
TrimFunc(s, f) | Runes, für die f true ist | links + rechts | Prädikat |
Faustregel: Sobald „ein ganzes Wort entfernen" gemeint ist, gehört der Aufruf nach TrimPrefix/TrimSuffix. Geht es um „beliebige Anführungs- oder Trennzeichen weg", ist Trim die richtige Wahl.
Beim Parsen halb-strukturierter Daten — etwa Werten aus einer rudimentären INI-Datei, Shell-Output oder einem ungewaschenen JSON-Snippet — sind Werte häufig noch von doppelten oder einfachen Anführungszeichen umschlossen. Trim mit Cutset "\"'" löst das in einer Zeile, unabhängig davon, ob doppelte oder einfache Quotes verwendet wurden.
package main
import (
"fmt"
"strings"
)
func unquote(s string) string {
return strings.Trim(s, "\"'")
}
func main() {
werte := []string{
`"mibeon.de"`,
`'localhost'`,
`"quoted'mixed"`,
`ohneQuotes`,
}
for _, v := range werte {
fmt.Printf("%-20s -> %q\n", v, unquote(v))
}
}"mibeon.de" -> "mibeon.de"
'localhost' -> "localhost"
"quoted'mixed" -> "quoted'mixed"
ohneQuotes -> "ohneQuotes"Beachte: Da der Cutset eine Menge ist, behandelt der Aufruf gemischte Quote-Paare genauso korrekt wie symmetrische — und Strings ohne Quotes bleiben unverändert. Für echtes JSON ist strconv.Unquote natürlich robuster (Escapes!), aber für hauseigene Formate ist diese Ein-Zeilen-Lösung Gold wert.
Vor allem beim Bauen von URLs oder beim Joinen von Pfadsegmenten will man führende und schliessende Slashes weghaben, bevor man Teile mit einem definierten Separator zusammensetzt. Trim(s, "/") ist hier die idiomatische Lösung und stabil gegenüber mehrfachen Slashes.
package main
import (
"fmt"
"strings"
)
func joinPath(parts ...string) string {
cleaned := make([]string, 0, len(parts))
for _, p := range parts {
p = strings.Trim(p, "/")
if p != "" {
cleaned = append(cleaned, p)
}
}
return "/" + strings.Join(cleaned, "/")
}
func main() {
fmt.Println(joinPath("/api/", "/v2/", "users/", "/42"))
fmt.Println(joinPath("docs", "///go///", "stdlib", "strings"))
}/api/v2/users/42
/docs/go/stdlib/stringsWeil Trim solange schneidet, wie das Zeichen matcht, fallen auch Mehrfach-Slashes wie ///go/// zuverlässig weg. Für komplexere Pfadlogik mit ..-Auflösung gehört der Aufruf zwar nach path.Clean, doch für reines Rand-Trimmen ist strings.Trim schneller und expliziter.
Cutset ist Menge, nicht Substring
Trim(s, "abc") entfernt jedes einzelne a, b, c an den Rändern — nicht das Wort abc. Reihenfolge und Duplikate im Cutset-String sind bedeutungslos.
Häufigste Verwechslung: TrimPrefix
Wer einen ganzen Substring vom Anfang abschneiden will, braucht TrimPrefix(s, prefix). Trim ist für diesen Zweck das falsche Werkzeug und führt zu subtilen Bugs.
Rune-aware: Multi-Byte funktioniert
Der Cutset wird als UTF-8 dekodiert. Umlaute, kyrillische Zeichen, Emojis — alles geht als Trim-Zeichen, ohne Byte-Gefriemel.
Entfernt solange Zeichen matchen
Trim hält an jeder Seite erst an, wenn das erste Nicht-Cutset-Zeichen erreicht ist. Mehrfaches Vorkommen am Rand wird komplett entfernt.
Leerer Cutset = s unverändert
Trim(s, "") gibt s ohne Allokation zurück. Whitespace wird dabei nicht behandelt — dafür gibt es TrimSpace.
TrimSpace ist der Whitespace-Spezialfall
Statt Trim(s, " \t\n\r") lieber TrimSpace(s) — das deckt sämtliche Unicode-Whitespaces inkl. NBSP korrekt ab.
Allokiert nur, wenn getrimmt wurde
Wenn am Rand nichts zu schneiden ist, wird s byteweise identisch zurückgegeben — der Header zeigt auf dasselbe Backing-Array.
Threadsafe / pure Funktion
Trim mutiert nichts und hängt nur von den Eingaben ab. Beliebig parallel aufrufbar, auch auf demselben Quell-String.