String と Rune
(初出: はじめての Go 言語 (on Windows) その4 - Qiita)
文字列を示す string は不変(immutable)なオブジェクトだが,中身は byte 配列である。 したがって以下のように
package main
import "fmt"
func main() {
nihongo := "日本語"
size := len(nihongo)
fmt.Printf("nihongo = %d bytes :", size)
for i := 0; i < size; i++ {
fmt.Printf(" %02x", nihongo[i])
}
fmt.Println()
}
nihongo = 9 bytes : e6 97 a5 e6 9c ac e8 aa 9e
このように(string なんて名前なのに)文字単位で情報を保持しているわけではないため,最初の2文字を取り出すつもりでうっかり
package main
import "fmt"
func main() {
nihongo := "日本語"
fmt.Printf("nihongo = %s\n", nihongo)
fmt.Printf("nippon = %s\n", nihongo[:2])
}
なんてなコードを書くと以下の結果になる。
nihongo = 日本語
nippon = ��
文字列を文字単位で扱うには rune 型を使う2。 いや,ルーンってどんだけ厨二… ゲフンゲフン。
ええつと,たとえばこんな感じ。
package main
import "fmt"
func main() {
nihongo := "日本語"
nihongoRune := []rune(nihongo)
size := len(nihongoRune)
fmt.Printf("nihongo = %d characters : ", size)
for i := 0; i < size; i++ {
fmt.Printf("%#U ", nihongoRune[i])
}
fmt.Printf("\n")
}
実行結果はこんな感じになる。
nihongo = 3 characters : U+65E5 '日' U+672C '本' U+8A9E '語'
または, string に対して for range 構文を使ってループを回すと rune 単位で取得できる。
package main
import "fmt"
func main() {
nihongo := "日本語"
for pos, runeValue := range nihongo {
fmt.Printf("%#U starts at byte position %d\n", runeValue, pos)
}
}
このコードの実行結果はこんな感じ。
U+65E5 '日' starts at byte position 0
U+672C '本' starts at byte position 3
U+8A9E '語' starts at byte position 6
rune 型の実体は int32 で,内部表現は Unicode の符号点(code point)になっている。 string と rune 配列は相互変換できるので,文字列を切り取る場合は
package main
import "fmt"
func main() {
nihongo := "日本語"
fmt.Printf("nihongo = %s\n", nihongo)
fmt.Printf("nippon = %s\n", string([]rune(nihongo)[:2]))
}
のように string
→ []
rune
→ string
と変換していけば安全に処理できる。
上のコードの実行結果はこんな感じ。
nihongo = 日本語
nippon = 日本
もう少し細かい処理が必要なら unicode/utf8
パッケージを使う手もある3。
ブックマーク
- Strings, bytes, runes and characters in Go - The Go Blog
- Go言語のstring, runeの正体とは? - golang - The Round
- Goでマルチバイトが混在した文字列をtruncateする - Qiita
- Go で UTF-8 の文字列を扱う - Qiita
参考図書
- プログラミング言語Go (ADDISON-WESLEY PROFESSIONAL COMPUTING SERIES)
- Alan A.A. Donovan (著), Brian W. Kernighan (著), 柴田 芳樹 (翻訳)
- 丸善出版 2016-06-20
- 単行本(ソフトカバー)
- 4621300253 (ASIN), 9784621300251 (EAN), 4621300253 (ISBN), 9784621300251 (ISBN)
- 評価
著者のひとりは(あの「バイブル」とも呼ばれる)通称 “K&R” の K のほうである。この本は Go 言語の教科書と言ってもいいだろう。
-
ちなみに Go 言語で取り扱う文字列の文字エンコーディングは UTF-8 が既定である。ソースコードがそもそも UTF-8 を要求しているし(つまりリテラルの文字列はかならず UTF-8 になる), string と rune の相互変換も文字列が UTF-8 であることを前提にしている。しかし,実際には string の実体はただの byte 配列であり,中身が UTF-8 文字列であることを保証しているわけではない。通常は,未知の文字列についてはいったん byte 配列に格納しておいて,何らかの方法で UTF-8 に変換してから string にキャストするのが安全である(または各文字エンコーディング用の独自 type を作るか)。なお,文字エンコーディングの変換については別の記事で改めて紹介する。 ↩︎
-
厳密には文字単位ではなく Unicode の符号点単位と言ったほうがいいかもしれない。 Unicode では結合文字(濁点記号や異体字セレクタなど)にもコードポイントが割り当てられているので,特に日本語で文字を意識した文字列操作を行う場合は注意が必要である。ぶっちゃけ面倒くさい。 ↩︎
-
ちなみに
strings
パッケージは内部でunicode/utf8
パッケージを使っているようだ。 ↩︎