strconv.Itoa ist die Umkehrung von Atoi und der schnellste Weg, aus einem int einen dezimalen String zu machen. Der Name stammt — wie Atoi — aus der C-Standardbibliothek: integer to ASCII. Funktional ist Itoa(n) exakt äquivalent zu FormatInt(int64(n), 10); intern ruft Itoa FormatInt mit Basis 10 auf und reicht das Ergebnis durch.

Die Funktion ist die idiomatische Wahl, wenn nur eine Zahl in einen String soll und keine besondere Formatierung (Padding, Vorzeichen-Zwang, Hex/Oktal/Binär) gebraucht wird. Im Vergleich zu fmt.Sprintf("%d", n) ist Itoa deutlich schneller — kein Format-String-Parsing, keine Reflection, keine Interface-Boxing. In Loggern, Serializern und Metric-Exportern ist der Unterschied messbar.

Die Signatur ist die kürzeste im Paket: ein int rein, ein String raus. Kein Fehler, weil eine int-zu-String-Konvertierung niemals fehlschlagen kann.

Go signatur.go
func Itoa(i int) string

Der Parameter ist int, nicht int64 — symmetrisch zu Atoi. Wer einen int64 formatieren will, ruft FormatInt direkt; wer einen kleineren Typ (int8, int32) hat, castet ihn vorher zu int.

Itoa produziert immer die kanonische Form ohne führende Nullen, mit Minus-Zeichen bei negativen Werten und ohne Plus-Zeichen bei positiven. Das ist die Notation, die Atoi wieder einlesen kann — Round-Trip-Garantie.

Go grundverhalten.go
package main

import (
	"fmt"
	"strconv"
)

func main() {
	cases := []int{0, 42, -17, 1_000_000, -2_147_483_648}
	for _, n := range cases {
		s := strconv.Itoa(n)
		fmt.Printf("%12d -> %q\n", n, s)
	}
}
Output
           0 -> "0"
          42 -> "42"
         -17 -> "-17"
     1000000 -> "1000000"
 -2147483648 -> "-2147483648"

Beachte: keine Tausender-Trenner, keine Padding-Nullen, kein wissenschaftliches Format. Wer Layout-Kontrolle braucht — etwa „auf 5 Stellen mit führenden Nullen" — nutzt fmt.Sprintf("%05d", n) oder eigenes Padding um Itoa.

Intern ist Itoa eine Ein-Zeilen-Hülle: return FormatInt(int64(i), 10). Der Cast auf int64 ist auf 64-Bit-Plattformen ein No-Op, auf 32-Bit-Plattformen eine Vorzeichen-Erweiterung — beides verlustfrei.

Go aequivalent.go
package main

import (
	"fmt"
	"strconv"
)

func main() {
	n := 12345

	a := strconv.Itoa(n)
	b := strconv.FormatInt(int64(n), 10)

	fmt.Println("Itoa:     ", a)
	fmt.Println("FormatInt:", b)
	fmt.Println("identisch:", a == b)
}
Output
Itoa:      12345
FormatInt: 12345
identisch: true

Faustregel: solange Basis 10 reicht und der Eingabewert ein int ist, ist Itoa der idiomatische Standard. Sobald eine andere Basis (16 für Hex, 2 für Binär) oder ein expliziter int64 ins Spiel kommt, gehört FormatInt an die Stelle.

Der Hauptgrund für Itoa über fmt.Sprintf("%d", n) ist Geschwindigkeit. fmt.Sprintf muss das Format-String parsen, per Reflection den Typ des Arguments bestimmen und über ein Interface dispatchen. Itoa macht nichts davon — es schreibt die Ziffern direkt in einen Buffer.

Go bench.go
package strconvbench

import (
	"fmt"
	"strconv"
	"testing"
)

var sink string

func BenchmarkSprintf(b *testing.B) {
	for i := 0; i < b.N; i++ {
		sink = fmt.Sprintf("%d", 1234567)
	}
}

func BenchmarkItoa(b *testing.B) {
	for i := 0; i < b.N; i++ {
		sink = strconv.Itoa(1234567)
	}
}
Output
BenchmarkSprintf-8       28_654_318    41.2 ns/op    16 B/op   2 allocs/op
BenchmarkItoa-8         158_421_004     7.5 ns/op     8 B/op   1 allocs/op

Die genauen Zahlen variieren je nach Hardware, aber die Verhältnisse sind robust: Itoa ist rund 5-6x schneller als Sprintf("%d", n) und allokiert nur halb so viel Speicher. In einem Logger mit Millionen Events pro Sekunde macht das den Unterschied zwischen „läuft" und „GC frisst die CPU".

Itoa produziert pro Aufruf einen neuen String — also eine Heap-Allokation. Die Größe ist proportional zur Ziffernanzahl: für int64-Max sind das maximal 20 Zeichen plus String-Header.

Im Hot-Path lohnt sich der Wechsel auf AppendInt, das in einen vorhandenen []byte schreibt und bei ausreichender Kapazität gar nicht allokiert. Für gelegentliche Konvertierungen — Debug-Output, einmalige Header — ist Itoa aber genau richtig: lesbar, sicher, kein Buffer-Management.

Go vs_append.go
package main

import (
	"fmt"
	"strconv"
)

func main() {
	// Klassisch: pro Aufruf eine Allokation
	a := strconv.Itoa(42)
	fmt.Println("via Itoa:    ", a)

	// Hot-Path: Allokationsfrei
	buf := make([]byte, 0, 32)
	buf = strconv.AppendInt(buf, 42, 10)
	fmt.Println("via Append:  ", string(buf))
}
Output
via Itoa:     42
via Append:   42

Faustregel: wer Itoa-Ergebnisse direkt weitergibt (Funktionsrückgabe, Map-Key, JSON-Feld), bleibt bei Itoa. Wer Itoa-Ergebnisse in einen größeren Output-Puffer kopiert, sollte direkt AppendInt benutzen — spart eine Allokation und einen Copy.

In Caches oder Lookup-Tabellen sind Strings oft die richtige Map-Key-Wahl (etwa für JSON-Serialisierung oder Vergleich mit User-Input). Itoa ist der direkteste Weg von numerischer ID zu String-Key.

Go map_keys.go
package main

import (
	"fmt"
	"strconv"
)

type User struct {
	Name string
}

func main() {
	users := map[string]User{}
	for id, name := range []string{"Anna", "Ben", "Carla"} {
		key := "user:" + strconv.Itoa(id+100)
		users[key] = User{Name: name}
	}

	for k, v := range users {
		fmt.Printf("%-10s -> %s\n", k, v.Name)
	}
}
Output
user:100   -> Anna
user:101   -> Ben
user:102   -> Carla

Die String-Konkatenation "user:" + Itoa(id) ist hier einfach und schnell. Bei sehr vielen Konkatenationen wäre strings.Builder mit AppendInt effizienter, für ein paar Dutzend Keys ist der Unterschied aber irrelevant.

Itoa selbst kennt kein Padding. Für „auf N Stellen mit Nullen auffüllen" ist fmt.Sprintf("%05d", n) der ehrliche Weg. Wer dasselbe ohne fmt will, kombiniert Itoa mit strings.Repeat.

Go padding.go
package main

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

func padLeft(n, width int) string {
	s := strconv.Itoa(n)
	if len(s) >= width {
		return s
	}
	return strings.Repeat("0", width-len(s)) + s
}

func main() {
	for _, n := range []int{7, 42, 1000, 99999} {
		fmt.Printf("%6d -> %s\n", n, padLeft(n, 5))
	}
}
Output
     7 -> 00007
    42 -> 00042
  1000 -> 01000
 99999 -> 99999

Achtung: bei negativen Zahlen wirkt das naive Padding nicht wie erwartet — padLeft(-7, 5) würde "00-7" ergeben statt "-0007". Für korrekte Vorzeichen-Behandlung lohnt sich entweder fmt.Sprintf("%05d", n) oder explizite Verzweigung über n < 0.

Interessantes

Itoa = FormatInt(int64(n), 10)

Bequemlichkeits-Hülle für den häufigsten Fall: Basis 10, plattformnatives int, ein Aufruf, kein Fehler.

Kein Fehler-Rückgabewert

Eine int-zu-Dezimal-Konvertierung kann nicht fehlschlagen — die Signatur ist (int) string, kein error nötig.

Round-Trip mit Atoi garantiert

Für jedes n gilt: Atoi(Itoa(n)) == n. Die Notation ist die kanonische Dezimal-Form ohne führende Nullen.

5-6x schneller als fmt.Sprintf

Ohne Format-String-Parsing und Reflection deutlich schneller. In Hot-Paths Pflicht.

Eine Allokation pro Aufruf

Der Rückgabe-String ist eine frische Heap-Allokation. Für Streaming-Output AppendInt nutzen.

Kein Padding, kein Format-Verb

Itoa produziert immer die minimale Darstellung. Wer Padding, Vorzeichen-Zwang oder Tausender-Trenner will, nimmt fmt.Sprintf.

Negative Zahlen mit Minus-Präfix

Negative int-Werte werden mit führendem - ausgegeben, positive ohne +. Symmetrisch zu Atoi.

Plattform-int, nicht int64

Der Parameter ist int — auf 32-Bit-Systemen also nur ±2.1 Mrd. Für int64 direkt FormatInt nutzen.

Weiterführende Ressourcen

Externe Quellen

/ Weiter

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

Zur Übersicht