fmt.Sscanln ist die zeilenorientierte Variante von fmt.Sscan: Sie liest aus einem String, parst whitespace-getrennte Tokens in die übergebenen Pointer — und stoppt zwingend am ersten Newline. Alles, was nach \n im Input folgt, wird verworfen. Damit ist Sscanln das Werkzeug der Wahl, wenn aus einem mehrzeiligen Puffer gezielt nur die erste Zeile strukturiert ausgewertet werden soll: Log-Header, Konfig-Anker, Protokoll-Frames. Die vollständige Referenz findest du auf pkg.go.dev/fmt#Sscanln.

Signatur und Semantik

Die Funktion ist im Paket fmt deklariert und folgt dem gleichen Pointer-Vertrag wie alle Scan-Varianten:

Go signature.go
func Sscanln(str string, a ...any) (n int, err error)

str ist der zu parsende Input-String, a eine variadische Liste von Pointern auf die Zielvariablen. Zurückgegeben werden die Anzahl erfolgreich gefüllter Argumente sowie ein Fehler, falls die Eingabe nicht zum erwarteten Muster passt. Der entscheidende Unterschied zu Sscan liegt nicht in der Signatur, sondern im Verhalten: Sscanln behandelt den Newline als harte Grenze.

Stopp am ersten Newline

Sscanln scannt Tokens, bis entweder alle Argumente gefüllt sind oder der erste \n im String erreicht ist. Folgezeilen sind für die Funktion nicht existent — sie werden nicht gelesen, nicht geprüft, nicht zurückgegeben. Das macht Sscanln deterministisch für zeilenweise Verarbeitung: Egal wie groß der Input-Puffer ist, geparst wird immer nur das erste Stück bis zum Zeilenende.

Go newline_stopp.go
package main

import "fmt"

func main() {
	input := "42 hallo\nzweite zeile wird ignoriert\ndritte auch"

	var n int
	var wort string
	count, err := fmt.Sscanln(input, &n, &wort)

	fmt.Printf("gelesen: %d Argumente, err=%v\n", count, err)
	fmt.Printf("n=%d, wort=%q\n", n, wort)
}
Output
gelesen: 2 Argumente, err=<nil>
n=42, wort="hallo"

Obwohl der String drei Zeilen enthält, sieht Sscanln nur die erste. Die Folgezeilen sind funktional unsichtbar. Das ist exakt das gewünschte Verhalten, wenn ein mehrzeiliger Block einen strukturierten Kopf hat, dem freier Text folgt.

Strict-Mode innerhalb der Zeile

Innerhalb der ersten Zeile ist Sscanln strikt: Befinden sich nach den erfolgreich geparsten Tokens noch weitere, nicht konsumierte Tokens vor dem Newline, liefert die Funktion einen Fehler. Das unterscheidet sie deutlich von Sscan, das überschüssige Tokens schweigend stehen lässt.

Go strict_mode.go
package main

import "fmt"

func main() {
	// Erwartet: 2 Tokens — geliefert: 3 auf einer Zeile
	input := "42 hallo extra\n"

	var n int
	var wort string
	count, err := fmt.Sscanln(input, &n, &wort)

	fmt.Printf("count=%d\n", count)
	fmt.Printf("err=%v\n", err)
}
Output
count=2
err=expected newline

Die beiden Argumente werden gefüllt, der Fehler expected newline signalisiert: Nach dem letzten erwarteten Token muss unmittelbar ein Newline folgen — kein weiteres Token. Diese Strenge ist ein Feature, kein Bug. Sie zwingt Aufrufer, das Zeilenformat exakt zu spezifizieren, und schützt vor stillem Datenverlust.

Sscan vs. Sscanln vs. Sscanf

Die drei Sscan-Familien-Funktionen unterscheiden sich in zwei Dimensionen: Wie wird die Eingabe begrenzt, und wie wird das Format spezifiziert?

FunktionZeilen-StoppFormat-StringExtra TokensUse Case
Sscanneinneinerlaubt, werden ignoriertbeliebige whitespace-Liste
Sscanlnja (\n)neinFehlerstrukturierte Einzelzeile
Sscanfformat-getriebenjaformat-abhängigpräzises Layout

Sscanln sitzt also in der Mitte: zeilenbasiert wie Scanln, aber ohne Format-String — passend für einfache, whitespace-separierte Header-Formate.

Use Case: Den Kopf eines mehrzeiligen Blocks lesen

Sscanln glänzt überall dort, wo ein Datenblock aus einem strukturierten Header und einem freien Rumpf besteht: Log-Einträge mit Header-Zeile und Stacktrace darunter, Konfig-Dateien mit Versionszeile gefolgt von Sektionen, Protokoll-Frames mit Steuerzeile und Payload. Statt den Block selbst zu zerlegen, übergibst du den ganzen Puffer an Sscanln — und die Funktion isoliert den Header für dich.

Log-Header aus mehrzeiligem Eintrag extrahieren

Ein typischer Log-Eintrag besteht aus einer maschinenlesbaren Header-Zeile (Zeitstempel, Level, Modul) und einem mehrzeiligen Body (Message, Stacktrace, Kontext). Sscanln liest exakt den Header.

Go log_header.go
package main

import "fmt"

func main() {
	logEintrag := `2026-05-22T08:14:03Z ERROR auth.session
session token expired for user 4711
context: ip=192.168.1.42, agent=curl/8.4.0
stacktrace:
  at validateToken (session.go:142)
  at handleRequest (handler.go:88)`

	var zeit, level, modul string
	n, err := fmt.Sscanln(logEintrag, &zeit, &level, &modul)
	if err != nil {
		fmt.Println("Header-Parse-Fehler:", err)
		return
	}

	fmt.Printf("Felder: %d\n", n)
	fmt.Printf("Zeit:   %s\n", zeit)
	fmt.Printf("Level:  %s\n", level)
	fmt.Printf("Modul:  %s\n", modul)
}
Output
Felder: 3
Zeit:   2026-05-22T08:14:03Z
Level:  ERROR
Modul:  auth.session

Der Stacktrace und der Kontext darunter sind für Sscanln unsichtbar — die Funktion liefert nur die drei Header-Felder. Will man den Body danach weiter verarbeiten, holt man ihn separat via strings.SplitN(logEintrag, "\n", 2)[1].

Konfig-Anker: Version und Datum aus erster Zeile

Viele textbasierte Konfig-Formate beginnen mit einer Ankerzeile, die das Format identifiziert: Versionsnummer, Erstellungsdatum, vielleicht ein Schema-Marker. Der Rest der Datei folgt eigenen Regeln. Sscanln extrahiert den Anker und überlässt den Rest dem eigentlichen Konfig-Parser.

Go konfig_anker.go
package main

import "fmt"

func main() {
	konfig := `mibeon-config v3.2 2026-05-22
[server]
host = localhost
port = 8080

[database]
dsn = postgres://localhost/app`

	var marker, version, datum string
	n, err := fmt.Sscanln(konfig, &marker, &version, &datum)
	if err != nil {
		fmt.Println("Anker fehlerhaft:", err)
		return
	}

	if marker != "mibeon-config" {
		fmt.Println("unbekanntes Format:", marker)
		return
	}

	fmt.Printf("Anker erkannt (%d Felder)\n", n)
	fmt.Printf("Version: %s\n", version)
	fmt.Printf("Datum:   %s\n", datum)
}
Output
Anker erkannt (3 Felder)
Version: v3.2
Datum:   2026-05-22

Das Muster ist robust: Schlägt der Anker fehl, brichst du sofort ab, ohne überhaupt den Konfig-Body anzufassen. Schlanker geht eine Format-Validierung kaum.

Stopp am ersten Newline

Sscanln liest exakt bis zum ersten \n im Input-String und ignoriert alles danach — der Rest des Puffers ist für die Funktion unsichtbar.

Pointer-Pflicht

Alle variadischen Argumente müssen Pointer auf die Zielvariablen sein; ein Wert statt eines Pointers führt zum Laufzeitfehler.

Strict innerhalb der Zeile

Übrig gebliebene Tokens vor dem Newline lösen expected newline aus — das Zeilenformat ist verbindlich.

Für zeilenbasierte Parser

Sscanln passt überall dort, wo ein Block aus strukturierter Header-Zeile und freiem Rumpf besteht — Log, Konfig, Protokoll.

Mehrzeilige Eingaben

Folgezeilen werden weder gelesen noch validiert; der Rumpf eines Blocks muss separat ausgewertet werden.

Zu wenige Tokens

Endet die Zeile, bevor alle Pointer befüllt sind, gibt Sscanln die bisher gelesene Anzahl plus einen Fehler zurück.

Alternative ohne Sscanln

strings.SplitN(s, "\n", 2) plus fmt.Sscan auf den ersten Teil ist äquivalent und gibt mehr Kontrolle über den Rest-Puffer.

In Production selten

Im Produktionscode dominiert Sscanf mit explizitem Format-String — Sscanln ist eher in Tools, Skripten und Quick-Parsern zu Hause.

Weiterführende Ressourcen

Externe Quellen

/ Weiter

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

Zur Übersicht