strings.Fields ist das Werkzeug für eine sehr häufige Aufgabe: einen Freitext in seine Wörter zerlegen, ohne sich um die genaue Form der Trenner kümmern zu müssen. Die Funktion spaltet s an jeder maximalen Sequenz von Whitespace-Runen — also überall dort, wo unicode.IsSpace true zurückgibt. Dazu zählen das gewöhnliche Leerzeichen, Tab, Newline, Carriage Return, vertikale Tabs, aber auch Unicode-Spezialitäten wie das geschützte Leerzeichen (NBSP, U+00A0) oder das Em-Space (U+2003).

Der entscheidende Unterschied zu strings.Split: aufeinanderfolgende Whitespaces werden als eine Trennstelle behandelt, und führende sowie nachgestellte Whitespaces erzeugen keine leeren Felder. Genau deshalb ist Fields für die Tokenisierung von menschlich getipptem Text das Mittel der Wahl — Mehrfach-Spaces, Tabulatoren und Zeilenumbrüche stören nicht.

Die Signatur ist denkbar schlicht: ein String hinein, ein Slice von Strings heraus. Es gibt keinen Trenner-Parameter — das Trennkriterium ist hart als „Unicode-Whitespace" verdrahtet und damit für die häufigste Tokenisierungs-Aufgabe vorkonfiguriert.

Go signatur.go
func Fields(s string) []string

Wer ein eigenes Trennkriterium braucht — etwa „trenne an allem, was kein Buchstabe ist" — greift stattdessen zu strings.FieldsFunc, das ein Prädikat func(rune) bool entgegennimmt. Fields selbst ist intern äquivalent zu FieldsFunc(s, unicode.IsSpace), nur mit einer optimierten Implementierung für den ASCII-Fast-Path.

Der Kern-Unterschied zu Split: Fields kollabiert beliebig lange Whitespace-Läufe zu einer einzigen logischen Trennstelle. Drei Spaces, ein Tab und ein Newline hintereinander erzeugen keine drei leeren Felder, sondern werden zusammen als ein Trenner gewertet.

Go sequenzen.go
package main

import (
	"fmt"
	"strings"
)

func main() {
	s := "a   b\t\nc"
	fmt.Printf("%q\n", strings.Fields(s))

	// Zum Vergleich: Split mit einzelnem Space
	fmt.Printf("%q\n", strings.Split(s, " "))
}
Output
["a" "b" "c"]
["a" "" "" "b\t\nc"]

Split zerlegt blind an jedem einzelnen Vorkommen des Trenners — drei Spaces werden zu drei Trennungen und liefern zwei leere Strings dazwischen. Fields hingegen sieht den ganzen Whitespace-Block als eine Naht und gibt nur die wirklich befüllten Token zurück. Genau das macht Fields für unstrukturierten Text robust.

Fields ist nicht auf ASCII beschränkt. Das Trennkriterium ist unicode.IsSpace, das auch exotischere Whitespace-Klassen aus der Unicode-Tabelle erfasst — darunter das NBSP (U+00A0, „non-breaking space"), das Em-Space (U+2003) oder das Ideographic Space (U+3000), das im japanischen Schriftsatz üblich ist.

Go unicode_whitespace.go
package main

import (
	"fmt"
	"strings"
)

func main() {
	//   = NBSP,   = Em-Space,   = Ideographic Space
	s := "alpha beta gamma delta"
	fmt.Printf("%q\n", strings.Fields(s))
}
Output
["alpha" "beta" "gamma" "delta"]

Dieses Verhalten ist meist erwünscht — gerade bei aus Webseiten kopiertem Text tauchen NBSPs auf, ohne dass der Nutzer es merkt. Wer ausschließlich an ASCII-Whitespace trennen will, muss zu FieldsFunc mit einem eigenen Prädikat greifen, das nur Space, Tab und Newline akzeptiert.

Eine wichtige Eigenschaft für defensive Code-Pfade: bei leerer oder reiner Whitespace-Eingabe liefert Fields niemals nil, sondern stets einen leeren, aber initialisierten Slice. Das erspart Nil-Checks vor range-Schleifen und macht den Rückgabewert sicher iterierbar.

Go leer.go
package main

import (
	"fmt"
	"strings"
)

func main() {
	a := strings.Fields("")
	b := strings.Fields("   \t\n  ")

	fmt.Printf("a=%q len=%d nil=%v\n", a, len(a), a == nil)
	fmt.Printf("b=%q len=%d nil=%v\n", b, len(b), b == nil)
}
Output
a=[] len=0 nil=false
b=[] len=0 nil=false

Beide Aufrufe geben einen Slice der Länge 0 zurück, der explizit nicht nil ist. In der Praxis ist das angenehm: ein for _, w := range strings.Fields(input) ist immer sicher, auch wenn input komplett leer oder nur Whitespace ist.

Fields alloziert grundsätzlich einen neuen Slice samt der enthaltenen Strings (die intern auf das Original-s zeigen — kein zusätzlicher Byte-Kopiervorgang, aber ein Header-Array pro Token). Für lange Texte und Hot-Paths kann das relevant werden, etwa wenn man Millionen von Log-Zeilen tokenisiert und gar nicht alle Token sammeln möchte.

Go fieldsseq.go
package main

import (
	"fmt"
	"strings"
)

func main() {
	s := "eins zwei drei vier fünf"

	// Klassisch: alloziert []string
	for _, w := range strings.Fields(s) {
		fmt.Println(w)
	}

	fmt.Println("---")

	// Go 1.24+: Iterator ohne Slice-Allokation
	for w := range strings.FieldsSeq(s) {
		fmt.Println("seq:", w)
	}
}
Output
eins
zwei
drei
vier
fünf
---
seq: eins
seq: zwei
seq: drei
seq: vier
seq: fünf

strings.FieldsSeq (seit Go 1.24) liefert einen iter.Seq[string], der die Token nacheinander streamt, ohne den umgebenden Slice zu materialisieren. Für reine Pipeline-Verarbeitung — tokenisieren, filtern, zählen — ist das die schlankere Variante.

Die drei Funktionen sehen ähnlich aus, lösen aber unterschiedliche Probleme. Die folgende Übersicht hilft bei der Wahl:

FunktionTrennkriteriumMehrfach-TrennerLeere TokenTypischer Einsatz
Fieldsunicode.IsSpacekollabiertnieFreitext-Tokenisierung
Splitexakter Substringerzeugt leere FeldermöglichCSV, fixe Trennzeichen
FieldsFuncPrädikat func(rune) boolkollabiertniebenutzerdefinierte Tokenizer

Faustregel: für menschlich getippten Text nimm Fields, für strukturierte Daten mit definiertem Trenner Split, und für alles dazwischen (z. B. „trenne an allem, was kein Buchstabe oder Ziffer ist") FieldsFunc.

Ein klassisches Mini-Programm: Häufigkeit jedes Wortes in einem Text zählen. Fields übernimmt die komplette Tokenisierung, eine map[string]int aggregiert. Vor der Zählung normalisieren wir die Tokens mit ToLower, damit „Hund" und „hund" zusammenfallen.

Go wortcounter.go
package main

import (
	"fmt"
	"sort"
	"strings"
)

func main() {
	text := `Der schnelle braune Fuchs springt
	         über den faulen Hund.
	         Der Hund schläft weiter.`

	counts := map[string]int{}
	for _, w := range strings.Fields(text) {
		w = strings.ToLower(strings.Trim(w, ".,;:!?\""))
		counts[w]++
	}

	// Stabile Ausgabe
	keys := make([]string, 0, len(counts))
	for k := range counts {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	for _, k := range keys {
		fmt.Printf("%-10s %d\n", k, counts[k])
	}
}
Output
braune     1
den        1
der        2
faulen     1
fuchs      1
hund       2
schläft    1
schnelle   1
springt    1
weiter     1
über       1

Beachtenswert: der zusätzliche strings.Trim entfernt Satzzeichen vom Wortrand — die ohne diesen Schritt zu Token wie "Hund." führen würden, die Fields selbst nicht kennt. Fields macht die Wort-Grenzen, die Normalisierung bleibt unsere Aufgabe.

Eine zweite häufige Anwendung: eine Eingabezeile in Argumente zerlegen, etwa für einen Mini-REPL oder ein Test-Harness, das Befehle aus einer Datei liest. Fields reicht aus, solange die Argumente keine Spaces enthalten — für echtes Shell-Parsing mit Quoting und Escapes ist eine Library wie mvdan.cc/sh oder das flag-Paket besser geeignet.

Go cli_split.go
package main

import (
	"fmt"
	"strings"
)

func dispatch(line string) {
	args := strings.Fields(line)
	if len(args) == 0 {
		return // leere Zeile, nichts zu tun
	}
	cmd, rest := args[0], args[1:]
	fmt.Printf("cmd=%q args=%q\n", cmd, rest)
}

func main() {
	dispatch("  add   3   4 ")
	dispatch("greet Welt")
	dispatch("\t\n")
	dispatch("quit")
}
Output
cmd="add" args=["3" "4"]
cmd="greet" args=["Welt"]
cmd="quit" args=[]

Die führenden und nachgestellten Whitespaces im ersten Aufruf werden stillschweigend geschluckt — kein leeres Token am Anfang, kein leeres am Ende. Die dritte Zeile besteht nur aus Whitespace, Fields liefert einen leeren Slice, und die len(args) == 0-Wache greift sauber. Genau dieses Verhalten ist es, was Fields für interaktive Eingaben so brauchbar macht.

unicode.IsSpace als Trenner

Fields trennt überall dort, wo unicode.IsSpace(r) true ergibt — Space, Tab, Newline, CR, vertikale Tabs und Unicode-Whitespace-Klassen.

Whitespace-Sequenzen kollabieren

Aufeinanderfolgende Whitespaces zählen als ein Trenner — "a b" liefert zwei Token, nicht vier mit Leerstrings dazwischen.

Keine leeren Token

Der Rückgabe-Slice enthält nie "" — anders als bei Split muss man leere Felder nicht herausfiltern.

Leere Eingabe liefert leeren Slice, nicht nil

Fields("") und Fields(" ") geben beide einen Slice der Länge 0 zurück, der nicht nil ist — range ist immer sicher.

Unicode-Whitespace inklusive NBSP

NBSP (U+00A0), Em-Space (U+2003) und Ideographic Space (U+3000) werden als Whitespace erkannt — wichtig bei aus Webseiten kopiertem Text.

Immer Allokation

Fields baut stets einen neuen []string auf — bei sehr langen Inputs oder Hot-Loops eine sichtbare Kostenstelle.

FieldsSeq für Iterator (Go 1.24+)

strings.FieldsSeq streamt die Token ohne Slice-Allokation — ideal für Filter-Pipelines, die nicht alle Wörter materialisieren müssen.

Für Custom-Prädikat FieldsFunc

Wenn das Trennkriterium nicht „Whitespace" ist (z. B. „nicht alphanumerisch"), nutzt man strings.FieldsFunc(s, pred) mit eigenem Runen-Prädikat.

Weiterführende Ressourcen

Externe Quellen

/ Weiter

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

Zur Übersicht