概要
Goで絵文字とかを扱ったことがなかったのでメモ
内容
Go言語での文字列はUTF-8で扱われる。UTF-8は1文字を1~4byteで表現する。
例えば、
"a"
は61
"あ"
はe3 81 82
"😀"
はf0 9f 98 80
そのため
s := "aあ😀" fmt.Println(len(s)) // 8
len
でbyte列の長さが得られる。
一方でbyte列の長さではなく、人間の目で認識している文字列の長さ、
すなわち、len(s)=3
が欲しい場合、
rune型にキャストしてカウントしてあげると良さそう。
ここで、rune
型はunicodeコードポイントを表す型(実態はint32)(よく知らんかった)
s := "aあ😀" fmt.Println(len([]rune(s))) // 3
または、utf8.RuneCountInString
でruneスライスの長さをカウントする事もできる
s := "aあ😀" fmt.Println(utf8.RuneCountInString(s)) // 3
しかし、これでは4byte以上で表現される絵文字のカウントがうまく行かない。例えば👨👩👧👦
は他の絵文字の組み合わせで表現されている(多分)
s := "👨👩👧👦" for i := 0; i < len(s); i++ { fmt.Printf("%x ", s[i]) // f0 9f 91 a8 e2 80 8d f0 9f 91 a9 e2 80 8d f0 9f 91 a7 e2 80 8d f0 9f 91 a6 } fmt.Println([]rune(s)) // [97 12354 128104 8205 128105 8205 128103 8205 128102]
このような絵文字も「1文字」としてカウントしたいとき、どうすればいいか調べていたら、ライブラリを発見。
runeスライスの長さが2以上の文字をも1文字としてカウントしてくれる。
grapheme cluster
とやらをカウントしているらしい。
func main() { s := ("aあ👨👩👧👦") fmt.Println(uniseg.GraphemeClusterCount(s)) // 3 }
おわりに
調べるうちに、Unicodeとかの理解が捗った。 todo: ライブラリの中身をもうちょい読む
参考
UTF-8 - MDN Web Docs 用語集: ウェブ関連用語の定義 | MDN The Go Programming Language Specification - The Go Programming Language