背景
こちらの記事 UTF-8 のテーブル(MySQL5.6)に竈門禰󠄀豆子が格納できない問題を調べてみたがきっかけで、
Golang
における絵文字の文字数を確認する方法を調査して見ました。
ちなみに、Golang
のソースコードや文字列リテラルは UTF-8
が採用されています。
UTF-8
は、英数字は 8bit で表現して、それ以外は 16,24,32bit で可変的に表現しています。
例えば、ひらがなカタカナ漢字の様な日本語は 2 byte のものが多いのですが、上記の記事にでもあった一部の異体字(wikipedia)は 4 byte だったりします。
あと、😺 とかの絵文字も 4byte のものが多いのですが、
🧑🤝🧑 や 👨👩👧👦 といった絵文字は複数の絵文字を組み合わせた絵文字で、 4byte 以上になったりします。
確認用コード
Go version: go1.17.8
package main
import (
"fmt"
"runtime"
"unicode/utf8"
"github.com/rivo/uniseg"
)
func main() {
fmt.Printf("Go version: %s\n", runtime.Version())
emoji := "😺"
// 文字数
println(1 == utf8.RuneCountInString(emoji))
// バイト数
println(4 == len(emoji))
// バイト数(😺 ) = 4bytes
println(4 == utf8.RuneLen('😺')) // RuneLen は,文字のエンコードに必要なバイト数を返します。
println(1 == utf8.RuneLen('a'))
emoji2 := "🧑🤝🧑"
println(5 == utf8.RuneCountInString(emoji2)) // 文字数(🧑🤝🧑 ) = 5 . (4bytesを超えた文字はutf8.RuneLen関数を使用できない)
println(18 == len(emoji2)) // バイト数(🧑🤝🧑 ) = 18bytes
// uniseg という Go 言語用パッケージがあって,これを使うと UTF-8 文字列を「文字」単位に切り出してくれる。
gr := uniseg.NewGraphemes(emoji2)
for gr.Next() {
rs := gr.Runes()
fmt.Printf("%v : %U\n", string(rs), rs)
}
// Output: 🧑🤝🧑 : [U+1F9D1 U+200D U+1F91D U+200D U+1F9D1]
// おまけ:string以外に []byte と []rune の文字数とバイト数も求め方も
buf := []byte("😺")
runes := []rune("😺")
println(1 == utf8.RuneCount(buf))
println(1 == len(runes))
println(4 == len(buf))
println(4 == len(string(runes)))
}