✒️
Goでマルチバイト文字列カウント
実装
-
len()
だと、バイト数がカウントされる。 - マルチバイトの文字列をカウントするには?
-
[]rune
にキャストして、len()
でカウントする。 - または、
utf8.RuneCountInString()
でカウントする。
-
main.go
package main
import (
"fmt"
"unicode/utf8"
)
func main() {
str := "こんにちは"
fmt.Printf("len(): %d (byte)\n", len(str))
fmt.Printf("[]rune(): %d (rune)\n", len([]rune(str)))
fmt.Printf("utf8.RuneCountInString(): %d (rune)\n", utf8.RuneCountInString(str))
}
実行結果
len(): 15 (byte)
[]rune(): 5 (rune)
utf8.RuneCountInString(): 5 (rune)
ベンチマーク
qiitaの過去記事(2014年)の記事がヒットしたが、さすがに2014年のベンチはGoのバージョンも違うので違う結果になりそうとのことで、実際にベンチを取ってみた。
ベンチは、下記gistを参考にgo.modを追加してGoのバージョンを指定して実行させる。gistコードありがとうございますmm
ベンチコードは、gitにアップしています。
$ go test -bench . -benchmem
goos: darwin
goarch: amd64
pkg: benchrunecount
cpu: Intel(R) Core(TM) i7-8559U CPU @ 2.70GHz
BenchmarkRunCountInString16ascii-8 88374483 13.47 ns/op 0 B/op 0 allocs/op
BenchmarkRunCountInString16multi-8 17920575 62.32 ns/op 0 B/op 0 allocs/op
BenchmarkRunCountInString32ascii-8 44421601 25.03 ns/op 0 B/op 0 allocs/op
BenchmarkRunCountInString32multi-8 10113091 116.1 ns/op 0 B/op 0 allocs/op
BenchmarkCastToRuneArray16ascii-8 100000000 10.50 ns/op 0 B/op 0 allocs/op
BenchmarkCastToRuneArray16multi-8 19589365 57.10 ns/op 0 B/op 0 allocs/op
BenchmarkCastToRuneArray32ascii-8 58416907 20.11 ns/op 0 B/op 0 allocs/op
BenchmarkCastToRuneArray32multi-8 9205329 127.1 ns/op 0 B/op 0 allocs/op
PASS
ok benchrunecount 9.988s
qiitaの記事(2014年)より、総じて3倍以上早くなっている。
マルチバイト文字列16文字においては、len([]rune())
は、utf8.RuneCountInString()
より若干早い。
だが、マルチバイト文字列32文字においては、utf8.RuneCountInString()
の方が早い。つまり、マルチバイト文字数が多くなるほどにutf8.RuneCountInString()
の方が早くなる。
結論
マルチバイト文字列をカウントする際は、意識せずutf8.RuneCountInString()
を使えば良さそう。
さいごに
今回は、「Goでマルチバイト文字列カウント」についてまとめました。
皆さんのGoライフの一助となれば幸いです。
twitterで開発について情報発信していますので、よかったらフォローしてくださいmm
参考
Discussion
「マルチバイト文字列カウント」とのことなので定義的に当てはまるかが微妙だったのですが、もしこれが文字数のことを指すのであれば、 👨👩👧👧 のように実態は複数のUnicodeコードポイントをつなげているだけで、人間には1文字に見えるような書記素クラスタの概念があるため、もっと厳密に文字数を数えるなら https://github.com/rivo/uniseg のようなライブラリを使ったりするのがおすすめです!
ご教授ありがとうございます!
👨👩👧👧 の存在は知りませんでした、、、