Unicode, UTF-8, Strings und Byte Slices

Go-Quelltexte sind per Definition in UTF-8. Damit sind auch String-Literale, wie sie in Quelltexten vorkommen, UTF-8.

text := "aö世界."

In diesem Beispiel ist der String text 10 Bytes lang: ein ASCII-Zeichen (ein Byte), ein Umlaut (zwei Bytes), zwei Kanji (je drei Bytes) und am Ende wieder ein ASCII-Zeichen.

fmt.Println(len(text)) // ergibt 10

Intern wird ein String als ein (unveränderlicher) Slice von Bytes repräsentiert. Der Variablentyp string wird jedoch, anders als beispielsweise []byte, als UTF-8 behandelt, was sich z.B. beim range Operator zeigt:

for i, c := range text {
    fmt.Printf("%d -> %c\n", i, c)
}

ergibt:

0 -> a
1 -> ö
3 -> 世
6 -> 界
9 -> .

Der range Operator iteriert also über die Unicode Codepunkte des UTF-8 Strings und gibt jeweils die Byte-Position des Zeichens (als Variable vom Typ int) und das Zeichen als Unicode (Typ rune bzw. int32) zurück.

Wenn man lieber direkt mit Unicode arbeiten möchte, bietet Go die Möglichkeit, einen String von UTF-8 nach Unicode zu konvertieren:

utext := []rune(text)
fmt.Println(len(utext))
for i, c := range utext {
    fmt.Printf("%d -> %c\n", i, c)
}

ergibt die Ausgabe:

5
0 -> a
1 -> ö
2 -> 世
3 -> 界
4 -> .

In Unicode ist der Text also nur noch fünf Zeichen lang, und der range Operator iteriert über die die fünf Elemente des []rune Slice. Als Unicode belegt der Text allerdings intern 20 Bytes (fünf Codepunkte zu je vier Bytes), während der String in UTF-8 nur zehn Bytes lang ist. Die Konvertierung von string nach []rune erfordert intern immer ein Umkopieren der Daten.

Ebenso lässt sich ein String in seine Byte-Repräsentation umwandeln:

bytes := []byte(text)
fmt.Println(len(bytes))
for i, c := range bytes {
    fmt.Printf("%d -> %d\n", i, c)
}

ergibt:

10
0 -> 97
1 -> 195
2 -> 182
3 -> 228
4 -> 184
5 -> 150
6 -> 231
7 -> 149
8 -> 140
9 -> 46

Der Slice ist also, wie der String, 10 Zeichen lang. Der range Operator iteriert aber über jedes einzelne Zeichen; diese sind u.U. nicht-druckbare Zeichen, daher werden sie in diesem Beispiel als Dezimalzahl ausgegeben.