web-dev-qa-db-ja.com

文字列の文字数を取得する方法は?

Goで文字列の文字数を取得するにはどうすればよいですか?

たとえば、文字列"hello"がある場合、メソッドは5を返す必要があります。 £はUTF-8で2バイトでエンコードされているため、len(str)はバイト数を返しますではなく文字数なので、len("£")は1ではなく2を返します。

126
Ammar

文字列をlen([]rune(YOUR_STRING))として[] runeに変換することにより、パッケージなしでルーンの数を取得する方法があります。

package main

import "fmt"

func main() {
    russian := "Спутник и погром"
    english := "Sputnik & pogrom"

    fmt.Println("count of bytes:",
        len(russian),
        len(english))

    fmt.Println("count of runes:",
        len([]rune(russian)),
        len([]rune(english)))

}

バイト数30 16

ルーン数16 16

34

書記素クラスタを考慮する必要がある場合は、regexpまたはunicodeモジュールを使用してください。書記素クラスタの長さには制限がないため、検証にはコードポイント(ルーン)またはバイト数のカウントも必要です。非常に長いシーケンスを削除する場合は、シーケンスが ストリームセーフテキスト形式 に準拠しているかどうかを確認してください。

package main

import (
    "regexp"
    "unicode"
    "strings"
)

func main() {

    str := "\u0308" + "a\u0308" + "o\u0308" + "u\u0308"
    str2 := "a" + strings.Repeat("\u0308", 1000)

    println(4 == GraphemeCountInString(str))
    println(4 == GraphemeCountInString2(str))

    println(1 == GraphemeCountInString(str2))
    println(1 == GraphemeCountInString2(str2))

    println(true == IsStreamSafeString(str))
    println(false == IsStreamSafeString(str2))
}


func GraphemeCountInString(str string) int {
    re := regexp.MustCompile("\\PM\\pM*|.")
    return len(re.FindAllString(str, -1))
}

func GraphemeCountInString2(str string) int {

    length := 0
    checked := false
    index := 0

    for _, c := range str {

        if !unicode.Is(unicode.M, c) {
            length++

            if checked == false {
                checked = true
            }

        } else if checked == false {
            length++
        }

        index++
    }

    return length
}

func IsStreamSafeString(str string) bool {
    re := regexp.MustCompile("\\PM\\pM{30,}") 
    return !re.MatchString(str) 
}
5
masakielastic

「キャラクター」とは何かの定義に大きく依存します。 「ルーンが文字に等しい」があなたの仕事に適している場合(通常はそうではありません)、VonCによる答えはあなたにぴったりです。それ以外の場合、Unicode文字列のルーンの数が興味深い値である状況はほとんどないことに注意してください。そして、そのような状況でも、可能であれば、ルーン文字が処理されるときに文字列を「トラバース」しながらカウントを推測して、UTF-8デコードの労力を倍増させないようにすることをお勧めします。

5
zzzz

特に絵文字(タイ語、韓国語、アラビア語などの一部の言語)を扱っている場合、これまでに提供された答えのどれも、期待どおりの文字数を提供しません。 VonCの提案 は次を出力します。

fmt.Println(utf8.RuneCountInString("????️‍????????????")) // Outputs "6".
fmt.Println(len([]rune("????️‍????????????"))) // Outputs "6".

これは、これらのメソッドがUnicodeコードポイントのみをカウントするためです。複数のコードポイントで構成できる文字は多数あります。

正規化パッケージ を使用する場合も同じです:

var ia norm.Iter
ia.InitString(norm.NFKD, "????️‍????????????")
nc := 0
for !ia.Done() {
    nc = nc + 1
    ia.Next()
}
fmt.Println(nc) // Outputs "6".

正規化は文字のカウントと実際には同じではなく、多くの文字を1コードポイント相当に正規化することはできません。

masakielasticの答え は近づきますが、修飾子のみを処理します(Rainbowフラグには修飾子が含まれているため、独自のコードポイントとしてカウントされません)。

fmt.Println(GraphemeCountInString("????️‍????????????"))  // Outputs "5".
fmt.Println(GraphemeCountInString2("????️‍????????????")) // Outputs "5".

Unicode文字列を(ユーザーが知覚する)文字、つまり書記素クラスターに分割する正しい方法は、 nicode Standard Annex#29 で定義されています。ルールは セクション3.1.1 にあります。 github.com/rivo/uniseg パッケージはこれらのルールを実装しているため、文字列の正しい文字数を決定できます。

fmt.Println(uniseg.GraphemeClusterCount("????️‍????????????")) // Outputs "2".
3
Oliver

文字列の長さを取得するには、いくつかの方法があります。

package main

import (
    "bytes"
    "fmt"
    "strings"
    "unicode/utf8"
)

func main() {
    b := "这是个测试"
    len1 := len([]rune(b))
    len2 := bytes.Count([]byte(b), nil) -1
    len3 := strings.Count(b, "") - 1
    len4 := utf8.RuneCountInString(b)
    fmt.Println(len1)
    fmt.Println(len2)
    fmt.Println(len3)
    fmt.Println(len4)

}

1
pigletfly

私は正規化を少し速くしようとしました:

    en, _ = glyphSmart(data)

    func glyphSmart(text string) (int, int) {
        gc := 0
        dummy := 0
        for ind, _ := range text {
            gc++
            dummy = ind
        }
        dummy = 0
        return gc, dummy
    }
0
Marcelloh