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.
// 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.
| Eingabe | Ergebnis | Kategorie |
|---|---|---|
1 | true | numerisch |
t | true | Kurzform |
T | true | Kurzform |
true | true | Langform |
True | true | Langform |
TRUE | true | Langform |
0 | false | numerisch |
f | false | Kurzform |
F | false | Kurzform |
false | false | Langform |
False | false | Langform |
FALSE | false | Langform |
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.
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)
}
}"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 syntaxBesonders 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.
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
}ignoriert: b=false (Bug — Eingabe war ungültig)
erkannt: keine gültige Boolean-EingabeDer 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.
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)
}
}"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 syntaxDer 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.
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))
}warn: FEATURE_Y="yes" nicht erkannt, nutze default false
FEATURE_X: true
FEATURE_Y: falseDer 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.
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.