✒️

Goでマルチバイト文字列カウント

2022/02/07に公開2

実装

  • len()だと、バイト数がカウントされる。
  • マルチバイトの文字列をカウントするには?
    1. []runeにキャストして、len()でカウントする。
    2. または、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のバージョンも違うので違う結果になりそうとのことで、実際にベンチを取ってみた。
https://qiita.com/reiki4040/items/b82bf5056ee747dcf713

ベンチは、下記gistを参考にgo.modを追加してGoのバージョンを指定して実行させる。gistコードありがとうございますmm
https://gist.github.com/reiki4040/6f225993b68805125e0f

ベンチコードは、gitにアップしています。
https://github.com/hayashikengo/benchrunecount

$ 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
https://twitter.com/kenbu05

参考

https://qiita.com/reiki4040/items/b82bf5056ee747dcf713
https://stackoverflow.com/questions/12668681/how-to-get-the-number-of-characters-in-a-string

Discussion

たふみたふみ

「マルチバイト文字列カウント」とのことなので定義的に当てはまるかが微妙だったのですが、もしこれが文字数のことを指すのであれば、 👨‍👩‍👧‍👧 のように実態は複数のUnicodeコードポイントをつなげているだけで、人間には1文字に見えるような書記素クラスタの概念があるため、もっと厳密に文字数を数えるなら https://github.com/rivo/uniseg のようなライブラリを使ったりするのがおすすめです!

kenbu🏠PICK/CTOkenbu🏠PICK/CTO

ご教授ありがとうございます!
👨‍👩‍👧‍👧 の存在は知りませんでした、、、