strconv.ParseUint ist die unsigned-Variante von ParseInt. Sie liest einen String als vorzeichenlose Ganzzahl in beliebiger Basis von 2 bis 36 und liefert sie als uint64. Da sie ausschließlich nicht-negative Werte kennt, steht die volle Breite des Zieltyps zur Verfügung — bei bitSize=64 reicht das bis 18_446_744_073_709_551_615, dem Maximum von uint64. ParseInt hingegen verliert das oberste Bit an das Vorzeichen und endet bei 9_223_372_036_854_775_807.
Der entscheidende Unterschied liegt jedoch nicht nur in der Range, sondern in der Syntax-Strenge: ParseUint lehnt jedes Vorzeichen ab. Sowohl -1 als auch +1 führen zu einem ErrSyntax. Wer Eingaben wie Hex-Adressen, Port-Nummern, IPv4-Oktette oder Byte-Größen parst, signalisiert mit ParseUint schon im Typ, dass negative Werte ein Fehler sind — die Validierung passiert beim Parsen, nicht erst in einer nachgelagerten Prüfung.
Die Signatur spiegelt ParseInt fast vollständig, lediglich der Rückgabetyp wechselt von int64 zu uint64. Der erste Rückgabewert trägt das Ergebnis, der zweite einen *strconv.NumError mit den Feldern Func, Num und Err — Letzteres ist entweder strconv.ErrSyntax (Eingabe ist kein gültiges Zahlenliteral der gewählten Basis) oder strconv.ErrRange (Zahl überschreitet die Range von uint(bitSize)).
package main
import (
"fmt"
"strconv"
)
func main() {
// func ParseUint(s string, base int, bitSize int) (uint64, error)
n, err := strconv.ParseUint("4096", 10, 64)
fmt.Println(n, err)
}4096 <nil>Der zurückgegebene uint64 muss bei kleinerem bitSize per Cast in den Zieltyp überführt werden — die Range-Prüfung garantiert, dass der Cast verlustfrei bleibt.
Drei Punkte trennen ParseUint von ParseInt. Erstens: kein Minus. -1 ist Syntax-Fehler, nicht etwa ErrRange. Zweitens: auch kein Plus. Während ParseInt("+1", 10, 64) problemlos 1 liefert, scheitert ParseUint("+1", 10, 64) mit ErrSyntax — die Funktion akzeptiert nur das nackte Zifferliteral. Drittens: die Range nutzt das volle Bit-Spektrum. Wo ParseInt bei bitSize=8 zwischen -128 und 127 arbeitet, deckt ParseUint 0 bis 255 ab.
package main
import (
"fmt"
"strconv"
)
func main() {
cases := []string{"-1", "+1", "255", "18446744073709551615"}
for _, s := range cases {
n, err := strconv.ParseUint(s, 10, 64)
fmt.Printf("%-22s -> %d, %v\n", s, n, err)
}
}-1 -> 0, strconv.ParseUint: parsing "-1": invalid syntax
+1 -> 0, strconv.ParseUint: parsing "+1": invalid syntax
255 -> 255, <nil>
18446744073709551615 -> 18446744073709551615, <nil>Beachte: ParseUint ist damit strenger als viele andere Parser. C's strtoul etwa akzeptiert ein führendes Minus und führt eine Zweierkomplement-Konvertierung durch — Go macht das bewusst nicht.
base arbeitet identisch zu ParseInt: Werte von 2 bis 36 wählen das Zahlensystem direkt, 0 aktiviert die Präfix-Erkennung. bitSize legt fest, wie groß der Wert maximal sein darf — 0 ist ein Alias für die native uint-Breite der Plattform (32 oder 64 Bit). Da kein Vorzeichen-Bit reserviert ist, verdoppelt sich die obere Grenze gegenüber ParseInt.
bitSize | max |
|---|---|
| 8 | 255 |
| 16 | 65 535 |
| 32 | 4 294 967 295 |
| 64 | 18 446 744 073 709 551 615 |
package main
import (
"fmt"
"strconv"
)
func main() {
for _, bs := range []int{8, 16, 32, 64} {
n, err := strconv.ParseUint("18446744073709551615", 10, bs)
fmt.Printf("bitSize=%2d -> %d, err=%v\n", bs, n, err)
}
}bitSize= 8 -> 255, err=strconv.ParseUint: parsing "18446744073709551615": value out of range
bitSize=16 -> 65535, err=strconv.ParseUint: parsing "18446744073709551615": value out of range
bitSize=32 -> 4294967295, err=strconv.ParseUint: parsing "18446744073709551615": value out of range
bitSize=64 -> 18446744073709551615, err=<nil>Bei ErrRange liefert ParseUint den Maximalwert des gewählten bitSize — nicht etwa 0. Das ist praktisch fürs Clamping, aber leicht zu übersehen: ein nicht geprüfter Fehler kann unbemerkt zum Maximum führen.
Mit base=0 schaut ParseUint auf das Präfix des Strings: 0x/0X heißt Hexadezimal, 0o/0O Oktal, 0b/0B Binär, ein führendes 0 ohne weiteren Buchstaben ebenfalls Oktal (Go-Syntax). Ohne Präfix wird Dezimal angenommen.
package main
import (
"fmt"
"strconv"
)
func main() {
literals := []string{"0xff", "0o17", "0b1010", "0755", "1000"}
for _, s := range literals {
n, err := strconv.ParseUint(s, 0, 64)
fmt.Printf("%-10s -> %d, %v\n", s, n, err)
}
}0xff -> 255, <nil>
0o17 -> 15, <nil>
0b1010 -> 10, <nil>
0755 -> 493, <nil>
1000 -> 1000, <nil>base=0 ist das richtige Werkzeug für Konfigurationsdateien, in denen Nutzer ihre bevorzugte Notation wählen können — etwa Permission-Bits als 0755 oder Speichergrenzen als 0x10000.
Sobald die Eingabe uint(bitSize)-Max übersteigt, gibt ParseUint ErrRange zurück und füllt das Ergebnis mit ebendiesem Maximum. Im Gegensatz zu ParseInt gibt es kein negatives Pendant — die untere Grenze ist immer 0 und nur durch das fehlende Vorzeichen unerreichbar (jeder Versuch mit - ist bereits ErrSyntax).
package main
import (
"errors"
"fmt"
"strconv"
)
func main() {
// uint8 max = 255
n, err := strconv.ParseUint("256", 10, 8)
fmt.Println("wert:", n)
fmt.Println("err: ", err)
if errors.Is(err, strconv.ErrRange) {
fmt.Println("typisierter Range-Fehler erkannt")
}
}wert: 255
err: strconv.ParseUint: parsing "256": value out of range
typisierter Range-Fehler erkanntDa das Ergebnis bei ErrRange auf den Maximalwert geklemmt wird, sollten Aufrufende den Fehler immer prüfen — sonst sieht eine fehlgeschlagene Eingabe „255" exakt wie eine gültige „255" aus.
Anders als ParseInt, das mit Atoi einen Shortcut für Basis-10/bitSize=0 hat, gibt es für ParseUint keine eigene Kurzform. Der direkte Aufruf strconv.ParseUint(s, 10, 64) ist der idiomatische Weg. Auch in die Gegenrichtung: FormatUint ist das Pendant, ein „Uitoa" existiert nicht.
package main
import (
"fmt"
"strconv"
)
func main() {
original := uint64(18446744073709551610)
s := strconv.FormatUint(original, 10)
fmt.Println("formatiert:", s)
back, err := strconv.ParseUint(s, 10, 64)
fmt.Println("zurück: ", back, err)
fmt.Println("identisch: ", back == original)
}formatiert: 18446744073709551610
zurück: 18446744073709551610 <nil>
identisch: trueDer Round-Trip FormatUint → ParseUint ist verlustfrei für jeden gültigen uint64-Wert — ein wichtiger Baustein für serialisierte Konfigurationen, Logs oder Protokollfelder.
Eine IPv4-Adresse besteht aus vier Dezimal-Oktetten zwischen 0 und 255. ParseUint mit bitSize=8 erledigt sowohl die Zahlenkonvertierung als auch die Range-Prüfung in einem Schritt — kein zusätzlicher Vergleich n > 255 notwendig.
package main
import (
"fmt"
"strconv"
"strings"
)
func parseIPv4(s string) ([4]uint8, error) {
parts := strings.Split(s, ".")
if len(parts) != 4 {
return [4]uint8{}, fmt.Errorf("ipv4 braucht 4 Oktette, hat %d", len(parts))
}
var out [4]uint8
for i, p := range parts {
n, err := strconv.ParseUint(p, 10, 8)
if err != nil {
return [4]uint8{}, fmt.Errorf("oktett %d (%q): %w", i, p, err)
}
out[i] = uint8(n)
}
return out, nil
}
func main() {
cases := []string{"192.168.1.1", "10.0.0.255", "256.0.0.1", "-1.0.0.0"}
for _, s := range cases {
ip, err := parseIPv4(s)
if err != nil {
fmt.Printf("%-15s -> FEHLER: %v\n", s, err)
continue
}
fmt.Printf("%-15s -> %v\n", s, ip)
}
}192.168.1.1 -> [192 168 1 1]
10.0.0.255 -> [10 0 0 255]
256.0.0.1 -> FEHLER: oktett 0 ("256"): strconv.ParseUint: parsing "256": value out of range
-1.0.0.0 -> FEHLER: oktett 0 ("-1"): strconv.ParseUint: parsing "-1": invalid syntaxDer Parser nutzt die Strenge von ParseUint als Validierungs-Werkzeug: -1 scheitert als ErrSyntax, 256 als ErrRange. Beide Klassen kommen über errors.Is typsicher beim Aufrufer an, ohne dass die Funktion eigene Vergleichslogik braucht.
Konfigurationen für Speichergrößen — etwa Cache-Limits, Heap-Reservierungen, Buffer-Längen — kommen in der Praxis oft als Hex-Literal (0x10000) oder als Dezimalwert (65536). Mit base=0 akzeptiert ein einziger Aufruf beide Notationen, und bitSize=64 erlaubt Größen bis nahe 16 Exabyte.
package main
import (
"errors"
"fmt"
"strconv"
)
type MemSize uint64
func ParseMemSize(s string) (MemSize, error) {
n, err := strconv.ParseUint(s, 0, 64)
if err != nil {
if errors.Is(err, strconv.ErrRange) {
return 0, fmt.Errorf("speichergröße %q übersteigt uint64-max", s)
}
return 0, fmt.Errorf("speichergröße %q ungültig: %w", s, err)
}
return MemSize(n), nil
}
func main() {
for _, s := range []string{"65536", "0x10000", "0o200000", "-1"} {
size, err := ParseMemSize(s)
if err != nil {
fmt.Printf("%-12s -> FEHLER: %v\n", s, err)
continue
}
fmt.Printf("%-12s -> %d Byte\n", s, size)
}
}65536 -> 65536 Byte
0x10000 -> 65536 Byte
0o200000 -> 65536 Byte
-1 -> FEHLER: speichergröße "-1" ungültig: strconv.ParseUint: parsing "-1": invalid syntaxDrei Notationen für 65536 werden korrekt erkannt. Die negative Eingabe scheitert schon syntaktisch — was hier exakt die gewünschte Semantik ist: negative Speichergrößen ergeben fachlich keinen Sinn.
Interessantes
Kein Vorzeichen erlaubt
Sowohl -1 als auch +1 führen zu ErrSyntax. Nur das nackte Zifferliteral wird akzeptiert.
Volle uint-Range
Bei bitSize=64 reicht der Wertebereich bis 18_446_744_073_709_551_615 — doppelt so weit wie ParseInt mit gleichem bitSize.
bitSize=0 ist plattform-nativ
bitSize=0 mappt auf die native uint-Breite (meist 64 Bit, auf 32-Bit-Plattformen 32).
base=0 erkennt Präfixe
0x/0X Hex, 0o/0O und führende 0 Oktal, 0b/0B Binär.
ErrRange klemmt auf Maximum
Bei Überlauf gibt ParseUint den Maximalwert des bitSize zurück, nicht 0. Fehler immer prüfen.
Kein Atoi-Pendant
Es gibt kein Atou o. Ä. — ParseUint(s, 10, 64) ist die idiomatische Form.
Range-Prüfung gratis
bitSize=8 ersetzt einen manuellen n > 255-Check. Ideal für Oktette, Bytes, kleine Counter.
Round-Trip mit FormatUint
FormatUint und ParseUint sind exakte Inversen für jeden uint64-Wert in gleicher Basis.