fmt.Sscan verhält sich zu einem String wie fmt.Scan zu Stdin: Es liest whitespace-getrennte Tokens und füllt damit die übergebenen Pointer. In praktischem Go-Code ist Sscan die mit Abstand häufigste Funktion der Scan-Familie, weil man die Eingabe meistens schon als String vorliegen hat — aus einer Datei, einer HTTP-Response, einem Test-Fixture oder einer Konfig-Zeile. Erst wenn echte interaktive Eingabe gefragt ist, kommt Scan selbst zum Zug. Die offizielle Referenz liegt auf pkg.go.dev/fmt#Sscan.

Signatur

func Sscan(str string, a ...any) (n int, err error)

Das erste Argument ist der Eingabe-String, danach folgen — wie bei allen Scan-Varianten — beliebig viele Pointer auf die Ziel-Variablen. Der Rückgabewert n zählt, wie viele Werte erfolgreich gelesen wurden; err ist nil, wenn alles geklappt hat. Anders als bei Scan wird kein Reader involviert: Der String wird intern in einen strings.Reader gewickelt und Token für Token verarbeitet.

Whitespace als Trenner

Wie Scan zerlegt Sscan die Eingabe an Whitespace — Leerzeichen, Tabs, Newlines, mehrere hintereinander zählen als ein Trenner. Das macht es ideal für einfache, durch Spaces separierte Werte und unbrauchbar, sobald die Eingabe Strukturzeichen wie = oder , enthält.

Go whitespace.go
package main

import (
	"fmt"
)

func main() {
	input := "  42   3.14    gopher  "

	var i int
	var f float64
	var s string

	n, err := fmt.Sscan(input, &i, &f, &s)
	fmt.Println("gelesen:", n, "err:", err)
	fmt.Printf("i=%d f=%.2f s=%q\n", i, f, s)
}
Output
gelesen: 3 err: <nil>
i=42 f=3.14 s="gopher"

Beachte: Die führenden und trailing Leerzeichen sind egal, ebenso wie die Anzahl der Spaces zwischen den Tokens. Sscan interessiert sich nur für die Sequenz nicht-leerer Tokens.

Pointer-Pflicht

Wie bei allen Scan-Funktionen müssen die Argumente Pointer sein, damit Sscan die Werte überhaupt schreiben kann. Ein Werte-Argument ohne & führt zur Laufzeit zum Fehler Sscan: type int is not a pointer.

Go pointer.go
package main

import "fmt"

func main() {
	var x int

	// Falsch — keine Adresse:
	_, err := fmt.Sscan("7", x)
	fmt.Println("ohne &:", err)

	// Richtig:
	_, err = fmt.Sscan("7", &x)
	fmt.Println("mit &:", err, "x=", x)
}
Output
ohne &: Sscan: type int is not a pointer
mit &: <nil> x= 7

Der Compiler kann diesen Fehler nicht abfangen, weil die variadischen Argumente ...any heißen. Erst zur Laufzeit prüft fmt per Reflection, ob ein Pointer übergeben wurde.

Wann Sscan, wann nicht

Sscan glänzt bei einfachen, whitespace-getrennten Eingaben mit fester Werte-Reihenfolge. Sobald Trennzeichen ins Spiel kommen oder einzelne Felder optional sind, wird es sperrig:

  • Whitespace-Liste ("42 3.14 gopher") — Sscan ist perfekt.
  • Strukturierte Form ("timeout=30 retries=3") — besser strings.Split plus strconv.
  • Festes Format mit Trennern ("2026-05-22") — Sscanf mit %d-%d-%d oder time.Parse.
  • JSON, YAML, TOML — die jeweiligen Pakete (encoding/json & Co.), niemals Sscan.
  • Großer Stream-Parsertext/scanner oder bufio.Scanner mit eigener Split-Funktion.

Die Faustregel: Sobald du anfängst, das Eingabeformat „zurechtzubiegen", bevor du Sscan aufrufst, ist es das falsche Werkzeug.

Sscan vs. Sscanln vs. Sscanf

Die drei String-Scanner unterscheiden sich in zwei Dimensionen: ob sie am Newline stoppen und ob sie ein Format-Template benutzen.

FunktionTrennerStopp am \nFormat-StringTypischer Einsatz
SscanWhitespace (inkl. \n)neinneinflache Werte-Liste in einer Zeile oder mehreren
SscanlnWhitespace ohne \nja — \n beendetneingenau eine Zeile, danach soll Schluss sein
Sscanflaut Format-Stringabhängig vom Formatjafeste Muster wie Datum, Logzeile, Protokoll
Go vergleich.go
package main

import "fmt"

func main() {
	in := "10 20\n30 40"

	var a, b, c, d int

	n1, _ := fmt.Sscan(in, &a, &b, &c, &d)
	fmt.Printf("Sscan:   n=%d a=%d b=%d c=%d d=%d\n", n1, a, b, c, d)

	var e, f, g, h int
	n2, err := fmt.Sscanln(in, &e, &f, &g, &h)
	fmt.Printf("Sscanln: n=%d err=%v e=%d f=%d\n", n2, err, e, f)

	var x, y int
	n3, _ := fmt.Sscanf("Punkt(10,20)", "Punkt(%d,%d)", &x, &y)
	fmt.Printf("Sscanf:  n=%d x=%d y=%d\n", n3, x, y)
}
Output
Sscan:   n=4 a=10 b=20 c=30 d=40
Sscanln: n=2 err=unexpected newline e=10 f=20
Sscanf:  n=2 x=10 y=20

Sscan zieht alle vier Werte über den Newline hinweg, Sscanln bricht beim Zeilenumbruch ab und meldet das per Fehler, Sscanf zerlegt die Klammer-Notation am Format-Template.

Praxis 1 — Konfig-Zeile parsen

Angenommen, aus einer Konfigdatei kommt eine Zeile "timeout=30 retries=3" und daraus sollen zwei Integer werden. Der naive Reflex, das mit Sscan zu erledigen, scheitert, weil timeout=30 als ein einzelnes Whitespace-Token gelesen wird — und Sscan müsste es in einen int schieben, was natürlich nicht klappt.

Go konfig_naiv.go
package main

import "fmt"

func main() {
	line := "timeout=30 retries=3"

	var timeout, retries int
	n, err := fmt.Sscan(line, &timeout, &retries)
	fmt.Println("n:", n)
	fmt.Println("err:", err)
}
Output
n: 0
err: expected integer

Erwartbar: Sscan will einen Integer und bekommt die Buchstabenfolge timeout=30. Für solche Eingaben ist die Kombination aus strings.Split und strconv der saubere Weg — sie macht das Format explizit und liefert pro Feld einen sprechenden Fehler.

Go konfig_richtig.go
package main

import (
	"fmt"
	"strconv"
	"strings"
)

func parseKV(token string) (string, int, error) {
	parts := strings.SplitN(token, "=", 2)
	if len(parts) != 2 {
		return "", 0, fmt.Errorf("kein key=value: %q", token)
	}
	v, err := strconv.Atoi(parts[1])
	if err != nil {
		return "", 0, fmt.Errorf("%s: %w", parts[0], err)
	}
	return parts[0], v, nil
}

func main() {
	line := "timeout=30 retries=3"

	cfg := map[string]int{}
	for _, tok := range strings.Fields(line) {
		k, v, err := parseKV(tok)
		if err != nil {
			fmt.Println("fehler:", err)
			continue
		}
		cfg[k] = v
	}

	fmt.Println("cfg:", cfg)
}
Output
cfg: map[retries:3 timeout:30]

strings.Fields zerlegt an Whitespace (genau wie Sscan es täte), strings.SplitN trennt Schlüssel und Wert am =, strconv.Atoi macht den Integer. Jeder Schritt ist einzeln testbar, und Fehler tragen den Schlüssel im Text — das hilft enorm beim Debuggen einer realen Konfig.

Praxis 2 — Test-Helper

In Tests ist die Welt umgekehrt: Hier kontrolliert man die Eingabe selbst, weiß genau, dass die Tokens whitespace-getrennt sind, und will mit möglichst wenig Boilerplate aus einer Fixture drei Werte gewinnen. Genau dafür ist Sscan gemacht.

Go test_helper.go
package main

import (
	"fmt"
)

// parseFixture liest "id score name" aus einer Testzeile.
func parseFixture(line string) (id int, score float64, name string, err error) {
	_, err = fmt.Sscan(line, &id, &score, &name)
	return
}

func main() {
	fixtures := []string{
		"1 99.5 alice",
		"2 87.0 bob",
		"3 73.25 carol",
	}

	for _, line := range fixtures {
		id, score, name, err := parseFixture(line)
		if err != nil {
			fmt.Println("fehler:", err)
			continue
		}
		fmt.Printf("id=%d score=%.2f name=%s\n", id, score, name)
	}
}
Output
id=1 score=99.50 name=alice
id=2 score=87.00 name=bob
id=3 score=73.25 name=carol

Der Helper ist drei Zeilen lang und bleibt lesbar, weil das Format trivial und unter eigener Kontrolle ist. In Production-Code, wo die Eingabe von außen kommt, wäre derselbe Helper zu fragil — dort gehört eine richtige Format-Validierung hin. In Tests ist dieser Pragmatismus aber genau richtig.

String als erstes Argument

Sscan(str, ...) nimmt die Eingabe direkt als String entgegen — kein Reader, kein File-Handle. Intern wird der String in einen strings.Reader gewickelt.

Pointer-Pflicht

Jedes Ziel braucht &var. Ohne Adresse gibt es einen Laufzeitfehler, weil der Compiler die any-Argumente nicht prüft.

Whitespace-getrenntes Parsing

Spaces, Tabs und Newlines sind austauschbare Trenner; Mehrfach-Whitespace wird wie ein einzelner behandelt.

Häufigste der Scan-Familie

In realem Go-Code begegnet einem Sscan deutlich öfter als Scan selbst, weil die Eingabe meistens schon als String vorliegt.

Type-aware Konvertierung

int, float64, string, bool werden anhand des Pointer-Typs automatisch erkannt und passend geparst.

Strukturiertes lieber anders

Für JSON, YAML oder eigene Token-Sprachen sind encoding/json und text/scanner die besseren Werkzeuge — Sscan ist ein Whitespace-Splitter, kein Parser.

n zählt erfolgreiche Werte

Der Rückgabewert n sagt, wie viele Pointer tatsächlich gefüllt wurden — nützlich, um Teilerfolge von Totalausfällen zu unterscheiden.

Ideal für Test-Helper

Bei kontrollierten Test-Eingaben ist Sscan der kürzeste Weg von Fixture-Zeile zu typisierten Variablen.

Weiterführende Ressourcen

Externe Quellen

/ Weiter

Zurück zu Das fmt-Paket — Formatierte I/O

Zur Übersicht