Go を勉強していると必ず出てくるのが rune という型です。
Go の文字列処理を理解するうえで避けて通れません。
この記事では、Go における文字列と rune の関係を整理していきます。
Go の string
型は UTF-8 のバイト列です。
gos := "あ"
fmt.Println(len(s)) // 3
fmt.Println([]byte(s)) // [227 129 130]
"あ"
は Unicode のコードポイント U+3042
[227 129 130]
)で表現されますlen(s)
は「文字数」ではなく「バイト数」を返します👉 Go の string
は「文字列」ではなく「UTF-8 の生バイト列」と理解するのが正確です。
rune
は int32
の別名例:
govar r rune = 'あ'
fmt.Println(r) // 12354 (U+3042)
fmt.Printf("%c\n", r) // あ
'あ'
という 文字リテラル は rune
型r
は「文字そのもの」ではなく「そのコードポイント(番号)」👉 Go で「1文字」を扱うなら rune
を使うのが基本。
型 | 中身 | 例 |
---|---|---|
string | UTF-8 のバイト列 | "あ" → [227 129 130] |
rune | Unicode コードポイント (int32) | 'あ' → 12354 (U+3042) |
for range
で文字列をループすると、UTF-8 を自動的にデコードして rune 単位で取り出します。
gos := "あ"
for i, r := range s {
fmt.Printf("index %d: rune %c (U+%04X)\n", i, r, r)
}
出力例:
plain textindex 0: rune あ (U+3042)
i
… 文字が始まる バイト位置r
… 文字のコードポイント (rune
)fmt.Printf
ではフォーマット指定子によって rune の出力方法が変わります。
gos := "あ"
for _, r := range s {
fmt.Printf("as char: %c\n", r) // 文字として
fmt.Printf("as int : %d\n", r) // 10進数
fmt.Printf("as hex : %x\n", r) // 16進数
fmt.Printf("as U+ : U+%04X\n", r) // Unicode表記風
}
出力:
plain textas char: あ
as int : 12354
as hex : 3042
as U+ : U+3042
言語 | 文字列の実体 | ループ時の単位 | 注意点 |
---|---|---|---|
Java | UTF-16 | char (16bit, UTF-16コードユニット) | サロゲートペアで1文字が2つに分かれる場合あり |
Python3 | Unicode (抽象的にコードポイント) | 1文字 = コードポイント | len(s) は文字数 |
Go | UTF-8 バイト列 | rune (コードポイント) | len(s) はバイト数。文字数は len([]rune(s)) |
rune
は Unicode の コードポイントを表す int32
len(string)
は「文字数」ではなく「バイト数」[]rune
に変換して扱う👉 「文字列 = バイト列」「文字 = rune(コードポイント)」と理解しておけば、Go での文字列処理はかなり見通しがよくなります。