12. マルチバイト文字の扱い
日本語などのマルチバイト文字の扱いについて見ていきます。
- 文字列(string)はread-onlyなbyteのスライスです。UnicodeやUTF-8などの定義されたフォーマットの値を保持しているとは限りません。
- Goのソースコードは常にUTF-8です。
- 文字リテラルはbyteレベルの有効なエスケープがない場合、常にUTF-8です。
- runeはint32のエイリアスで、Unicodeコードポイントを表します。
Goの標準ライブラリの中で、UTF-8は特別な扱いとなっています。
通常のforループでは1バイト毎のインデックスとなりますが、rangeループではrune毎のインデックスとなります。
package main import ( "fmt" ) const nihongo = "日本語" func main() { // 通常のforループの場合、バイト毎の処理 for i := 0; i < len(nihongo); i++ { // %x 16進数表示 fmt.Printf("%x starts at byte position %d\n", nihongo[i], i) } // e6 starts at byte position 0 // 97 starts at byte position 1 // a5 starts at byte position 2 // e6 starts at byte position 3 // 9c starts at byte position 4 // ac starts at byte position 5 // e8 starts at byte position 6 // aa starts at byte position 7 // 9e starts at byte position 8 // rangeでのループの場合、rune毎のインデックス for index, runeValue := range nihongo { // %#U Unicodeコードポイントおよび fmt.Printf("%#U starts at byte position %d\n", runeValue, index) } // U+65E5 '日' starts at byte position 0 // U+672C '本' starts at byte position 3 // U+8A9E '語' starts at byte position 6 }
文字数をカウントする場合、len() ではバイト数を返しますが、
unicode/utf8 パッケージの RuneCountInString を使うことでruneでのカウントを返します。
package main import ( "fmt" "unicode/utf8" ) const nihongo = "日本語" func main() { // len() はbyte数なので len == 9 fmt.Printf("[]bytes %d", len(nihongo)) // RuneCountInString() はrune数なので len == 3 fmt.Printf("rune %d", utf8.RuneCountInString(nihongo)) }
マルチバイト文字列を扱うときには、runeを適切に使うようにしましょう。