背景

こちらの記事 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)))
}

Golang Playground Demo

元記事

Golang-文字数を数える方法,4バイト以上の絵文字もある

bannerAds