外出先で「文字」を数えようとしています。つまり、文字列に印刷可能な「グリフ」または「構成文字」(または誰かが通常文字と考えるもの)が1つ含まれている場合、1を数えたいと思います。たとえば、文字列「こんにちは、世???」 ?????????界」は、11文字あるため、11と数える必要があります。人間はこれを見て、11個のグリフがあると言います。
utf8.RuneCountInString()は、ASCII、アクセント、アジア文字、さらには絵文字を含め、ほとんどの場合にうまく機能します。ただし、私が理解しているように、ルーンは文字ではなくコードポイントに対応します。基本的な絵文字を使おうとすると機能しますが、肌の色が異なる絵文字を使用すると、カウントが間違ってしまいます: https://play.golang.org/p/aFIGsB6MsO
私が読んだものから ここ と ここ 以下はうまくいくはずですが、それでも正しい結果が得られていないようです(過大評価されています):
func CountCharactersInString(str string) int {
var ia norm.Iter
ia.InitString(norm.NFC, str)
nc := 0
for !ia.Done() {
nc = nc + 1
ia.Next()
}
return nc
}
これも機能しません:
func GraphemeCountInString(str string) int {
re := regexp.MustCompile("\\PM\\pM*|.")
return len(re.FindAllString(str, -1))
}
私はObjectiveCでこれに似たものを探しています:
+ (NSInteger)countCharactersInString:(NSString *) string {
// --- Calculate the number of characters enterd by user and update character count label
NSInteger count = 0;
NSUInteger index = 0;
while (index < string.length) {
NSRange range = [string rangeOfComposedCharacterSequenceAtIndex:index];
count++;
index += range.length;
}
return count;
}
これを可能にするパッケージを作成しました: https://github.com/rivo/uniseg 。 nicode Standard Annex#29 で指定されたルールに従って文字列を分割します。これは、探しているものです。これがあなたの場合にそれをどのように使うかです:
package main
import (
"fmt"
"github.com/rivo/uniseg"
)
func main() {
fmt.Println(uniseg.GraphemeClusterCount("Hello, 世????????????界"))
}
これにより、期待どおりに11
が出力されます。
strings.Count を試しましたか?
package main
import (
"fmt"
"strings"
)
func main() {
fmt.Println(strings.Count("Hello, 世????????界", "????")) // Returns 2
}
APIドキュメントの例への参照。 https://golang.org/pkg/unicode/utf8/#example_DecodeLastRuneInString
package main
import (
"fmt"
"unicode/utf8"
)
func main() {
str := "Hello, 世????界"
count := 0
for len(str) > 0 {
r, size := utf8.DecodeLastRuneInString(str)
count++
fmt.Printf("%c %v\n", r, size)
str = str[:len(str)-size]
}
fmt.Println("count:",count)
}