Wen

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

N views

背景

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

本記事は 「表示 - 非営利 - 改変禁止 4.0 国際 (CC BY-NC-ND 4.0)」 を採用。