strconv.ParseBool ist der vielleicht striktest gehaltene Parser in Gos Standardbibliothek. Er akzeptiert genau 11 Zeichenketten und lehnt alles andere konsequent mit einem *NumError ab. Diese gewollte Knauserigkeit ist kein Mangel, sondern Design: der Parser soll keine Interpretationsspielräume zulassen und keine sprachabhängigen Annahmen treffen.

Wer von Pythons bool("false") == True oder JavaScripts Boolean("0") === true kommt, wird das Verhalten als befreiend erleben. ParseBool interpretiert nicht — es matcht. Wer tolerantere Eingaben wie yes/no/on/off braucht, muss einen eigenen Wrapper schreiben. Genau das werden wir in den Praxis-Sektionen tun.

Die Funktion lebt im Paket strconv und hat eine simple, idiomatische Go-Signatur. Sie nimmt einen String entgegen und gibt das Ergebnis-Tupel (bool, error) zurück — das in Go übliche Muster für Operationen, die fehlschlagen können.

Go signatur.go
// ParseBool returns the boolean value represented by the string.
// It accepts 1, t, T, TRUE, true, True, 0, f, F, FALSE, false, False.
// Any other value returns an error.
func ParseBool(str string) (bool, error)

Der Vertrag ist scharf umrissen: die Dokumentation listet die akzeptierten Tokens namentlich auf. Das ist ungewöhnlich für eine stdlib-API und unterstreicht, wie absichtsvoll die Liste gewählt wurde. Alles, was nicht auf der Liste steht, ist ein Fehler — Punkt. Keine Leerzeichen-Toleranz, keine Groß-/Kleinschreibungs-Heuristik jenseits der explizit erlaubten Varianten.

Hier ist die vollständige Liste. Sie lässt sich in drei semantische Gruppen aufteilen: numerisch, Kurzform, Langform.

EingabeErgebnisKategorie
1truenumerisch
ttrueKurzform
TtrueKurzform
truetrueLangform
TruetrueLangform
TRUEtrueLangform
0falsenumerisch
ffalseKurzform
FfalseKurzform
falsefalseLangform
FalsefalseLangform
FALSEfalseLangform

Auffällig: bei der Langform sind nur drei Schreibweisen pro Wert erlaubt — komplett klein, Title-Case und komplett groß. Mischformen wie tRuE oder falSe werden abgelehnt. Bei der Kurzform existieren nur die Ein-Buchstaben-Varianten in beiden Casings; tr oder fa sind ungültig.

Die Liste der häufig erwarteten, aber abgelehnten Tokens ist länger als die der erlaubten. Wer das nicht weiß, läuft in subtile Bugs — gerade bei Config-Parsing aus YAML, ENV-Variablen oder CLI-Flags.

Go abgelehnt.go
package main

import (
	"fmt"
	"strconv"
)

func main() {
	inputs := []string{
		"yes", "no", "on", "off",
		"y", "n", "Y", "N",
		"tRuE", "FaLsE",
		"  true  ", "true\n",
		"", "2", "-1",
	}

	for _, s := range inputs {
		b, err := strconv.ParseBool(s)
		fmt.Printf("%-12q -> %-5v  err=%v\n", s, b, err)
	}
}
Output
"yes"        -> false  err=strconv.ParseBool: parsing "yes": invalid syntax
"no"         -> false  err=strconv.ParseBool: parsing "no": invalid syntax
"on"         -> false  err=strconv.ParseBool: parsing "on": invalid syntax
"off"        -> false  err=strconv.ParseBool: parsing "off": invalid syntax
"y"          -> false  err=strconv.ParseBool: parsing "y": invalid syntax
"tRuE"       -> false  err=strconv.ParseBool: parsing "tRuE": invalid syntax
"  true  "   -> false  err=strconv.ParseBool: parsing "  true  ": invalid syntax
"true\n"     -> false  err=strconv.ParseBool: parsing "true\n": invalid syntax
""           -> false  err=strconv.ParseBool: parsing "": invalid syntax
"2"          -> false  err=strconv.ParseBool: parsing "2": invalid syntax

Besonders tückisch: Whitespace wird nicht getrimmt. Ein nachgelagertes \n aus einer Datei oder ein führendes Leerzeichen aus einer Eingabezeile killt den Parse sofort. Wer Roh-Input verarbeitet, muss vorher selbst strings.TrimSpace anwenden.

Eine wichtige Eigenheit: bei einem Fehler gibt ParseBool das Tupel (false, *NumError) zurück. Der false-Wert ist hier nicht semantisch — er ist schlicht der Go-Nullwert des Rückgabe-Typs.

Go nullwert.go
package main

import (
	"errors"
	"fmt"
	"strconv"
)

func main() {
	// FALSCH: ignoriert Fehler, behandelt "yes" als false
	b, _ := strconv.ParseBool("yes")
	fmt.Printf("ignoriert: b=%v (Bug — Eingabe war ungültig)\n", b)

	// RICHTIG: Fehler ist die Wahrheit
	b, err := strconv.ParseBool("yes")
	if errors.Is(err, strconv.ErrSyntax) {
		fmt.Println("erkannt: keine gültige Boolean-Eingabe")
	}
	_ = b
}
Output
ignoriert: b=false (Bug — Eingabe war ungültig)
erkannt: keine gültige Boolean-Eingabe

Der zurückgegebene Fehler ist konkret ein *strconv.NumError mit den Feldern Func (hier ParseBool), Num (der Original-String) und Err (meist strconv.ErrSyntax). Das erlaubt feingranulares Fehler-Matching mit errors.Is(err, strconv.ErrSyntax).

Wenn die 11-Token-Welt zu eng ist — etwa beim Lesen von ENV-Variablen, wo yes/no oder on/off üblich sind — schreibt man einen Wrapper. Das Muster: strings.TrimSpace + strings.ToLower + switch über bekannte Synonyme, mit Fallback auf ParseBool.

Go loose.go
package main

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

// ParseBoolLoose akzeptiert zusätzlich yes/no/on/off/y/n/ja/nein
func ParseBoolLoose(s string) (bool, error) {
	switch strings.ToLower(strings.TrimSpace(s)) {
	case "yes", "y", "on", "ja", "j":
		return true, nil
	case "no", "n", "off", "nein":
		return false, nil
	}
	// Fallback: stdlib übernimmt die kanonischen 11 Tokens
	return strconv.ParseBool(strings.TrimSpace(s))
}

func main() {
	for _, s := range []string{"yes", "JA", "off", "true", "1", "vielleicht"} {
		b, err := ParseBoolLoose(s)
		fmt.Printf("%-12q -> %v  err=%v\n", s, b, err)
	}
}
Output
"yes"        -> true  err=<nil>
"JA"         -> true  err=<nil>
"off"        -> false  err=<nil>
"true"       -> true  err=<nil>
"1"          -> true  err=<nil>
"vielleicht" -> false  err=strconv.ParseBool: parsing "vielleicht": invalid syntax

Der Trick ist die Reihenfolge: erst die eigenen Synonyme prüfen, dann an strconv.ParseBool durchreichen. So bleibt die Fehlerkette intakt (errors.Is(err, strconv.ErrSyntax) funktioniert weiterhin), und die kanonischen Tokens behalten ihre Bedeutung.

Ein klassischer Anwendungsfall: ein Feature-Flag aus der Umgebung lesen. Hier zeigt sich das Zusammenspiel aus ParseBool, errors.Is und einem sinnvollen Default.

Go envbool.go
package main

import (
	"errors"
	"fmt"
	"os"
	"strconv"
	"strings"
)

// EnvBool liest eine ENV-Variable als Boolean.
// Leer oder nicht gesetzt -> default. Ungültig -> default + Log-Hinweis.
func EnvBool(key string, def bool) bool {
	raw, ok := os.LookupEnv(key)
	if !ok || strings.TrimSpace(raw) == "" {
		return def
	}
	b, err := strconv.ParseBool(strings.TrimSpace(raw))
	if errors.Is(err, strconv.ErrSyntax) {
		fmt.Fprintf(os.Stderr, "warn: %s=%q nicht erkannt, nutze default %v\n", key, raw, def)
		return def
	}
	return b
}

func main() {
	os.Setenv("FEATURE_X", "true")
	os.Setenv("FEATURE_Y", "yes") // wird abgelehnt!

	fmt.Println("FEATURE_X:", EnvBool("FEATURE_X", false))
	fmt.Println("FEATURE_Y:", EnvBool("FEATURE_Y", false))
}
Output
warn: FEATURE_Y="yes" nicht erkannt, nutze default false
FEATURE_X: true
FEATURE_Y: false

Der Default-Wert wird zweimal verwendet: bei fehlender Variable und bei ungültiger Variable. Das ist eine bewusste Entscheidung — ein typisch deployment-freundliches Verhalten, das niemals mit Panik abbricht, aber gleichzeitig den Fehlerfall protokolliert, damit fehlerhafte Configs nicht still schlummern.

Wer CLI-Tools baut, kennt das Problem: Benutzer tippen --confirm=yes und erwarten, dass es funktioniert. Mit einer kleinen Adapter-Schicht über flag.Value lässt sich ParseBool zur tolerant-akzeptierenden Flag-Variante erweitern.

Go yesno_flag.go
package main

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

type YesNoBool bool

func (y *YesNoBool) String() string {
	if y == nil {
		return "false"
	}
	return strconv.FormatBool(bool(*y))
}

func (y *YesNoBool) Set(s string) error {
	switch strings.ToLower(strings.TrimSpace(s)) {
	case "yes", "y", "on":
		*y = true
		return nil
	case "no", "n", "off":
		*y = false
		return nil
	}
	b, err := strconv.ParseBool(strings.TrimSpace(s))
	if err != nil {
		return fmt.Errorf("erwarte true/false/yes/no, bekam %q", s)
	}
	*y = YesNoBool(b)
	return nil
}

func (y *YesNoBool) IsBoolFlag() bool { return true }

func main() {
	var confirm YesNoBool
	flag.Var(&confirm, "confirm", "Bestätigung (true/false/yes/no)")
	flag.Parse()
	fmt.Println("confirm =", bool(confirm))
}

Die Methode IsBoolFlag() ist hier entscheidend: sie sorgt dafür, dass flag das Argument als Bool-Flag behandelt, sodass auch --confirm (ohne Wert) als true funktioniert. Die Fehlermeldung im Set-Fall ist bewusst benutzerfreundlich formuliert — ein roher *NumError mit strconv.ParseBool: parsing wäre für einen CLI-User verwirrend.

Interessantes

Genau 11 Tokens

ParseBool akzeptiert 1/0, t/T/f/F, true/True/TRUE, false/False/FALSE — und sonst nichts. Mischformen wie tRuE schlagen fehl.

Kein TrimSpace eingebaut

Führender oder nachgelagerter Whitespace führt zu ErrSyntax. Bei Roh-Input von Datei/Netzwerk vorher strings.TrimSpace anwenden.

false ist kein Signal

Im Fehlerfall ist der bool-Rückgabewert immer false — das ist der Nullwert, nicht das Parse-Ergebnis. Niemals den Fehler ignorieren.

NumError mit ErrSyntax

Der Fehler ist ein *strconv.NumError, der via errors.Is(err, strconv.ErrSyntax) matchbar ist. Felder: Func, Num, Err.

Yes/No braucht Wrapper

yes, no, on, off, y, n werden alle abgelehnt. Wer das braucht, schreibt eine eigene Funktion mit switch über strings.ToLower.

Gegenstück FormatBool

Die Umkehrfunktion ist strconv.FormatBool(bool) string und liefert immer true oder false — also nur zwei der 11 Akzeptanz-Tokens.

Sinnvoll für strikte Configs

In Kontexten, in denen Mehrdeutigkeit gefährlich ist (Security-Flags, Deployment-Switches), ist die Strenge ein Feature, kein Bug.

Quelle ist trivial

Die Implementierung in go/src/strconv/atob.go ist ein simples switch über die 11 Strings — kein Regex, keine State-Machine. Daher auch sehr performant.

Weiterführende Ressourcen

Externe Quellen

/ Weiter

Zurück zu Das strconv-Paket — String-Zahl-Konvertierung

Zur Übersicht