文字列内の文字数を取得する方法
-
12-12-2019 - |
質問
GOの文字列の文字数を取得できますか?
たとえば、文字列"hello"
を持っている場合、メソッドは5
を返します。len(str)
がBytes の数を返し、の数を返しているため、len("£")
は1文字の代わりに2を返します。
解決
RuneCountInString
を試すことができます。
は、p の実行数を返します
、このスクリプト: "World"の長さが6になる可能性があります。中国語で書かれたとき:「世界」)しかし、そのルーン数は2:
.package main import "fmt" import "unicode/utf8" func main() { fmt.Println("Hello, 世界", len("世界"), utf8.RuneCountInString("世界")) }
実際には、鋳造のタイプで走行中の
len()
を実行できます。
len([]rune("世界"))
は2
を印刷します。 GO 1.3のレートで。
と CL 108985 (2018年5月、GO 1.11) 、
len([]rune(string))
が最適化されました。 (修正問題24923 )コンパイラは、
len([]rune(string))
パターンを自動的に検出し、それをR:=範囲の呼び出しに置き換えます。文字列内の実行数をカウントするための新しいランタイム関数を追加します。 コンパイラを変更してPattern
len([]rune(string))
を検出します そしてそれを新しいRuneカウントランタイム関数に置き換えます。.RuneCount/lenruneslice/ASCII 27.8ns ± 2% 14.5ns ± 3% -47.70% (p=0.000 n=10+10) RuneCount/lenruneslice/Japanese 126ns ± 2% 60ns ± 2% -52.03% (p=0.000 n=10+10) RuneCount/lenruneslice/MixedLength 104ns ± 2% 50ns ± 1% -51.71% (p=0.000 n=10+9)
Stefan Steiger ブログの投稿を指しています "
GO " のテキストの正規化 文字とは何ですか?
文字列ブログ投稿、文字は複数のルーンにまたがることができます。
たとえば、 'e
'と '◌◌'(Acute "\ U0301")は組み合わせることができ、「é」(「e\u0301
」)を作成できます(NFD)。 一緒にこれら2つのルーンは1文字です。キャラクタの定義はアプリケーションによって異なります。
正規化 私たちは次のように定義します。
- スターターで始まる一連の実行
- 他のRuneと逆方向に変更または結合しないRune、
- 続いて、おそらく空の非組みの順序、つまり(通常はアクセント)を実行します。
正規化アルゴリズムは時に1文字を処理する。
そのパッケージとその
Iter
Type 、実際の「文字」の数は次のとおりです。.package main import "fmt" import "golang.org/x/text/unicode/norm" func main() { var ia norm.Iter ia.InitString(norm.NFKD, "école") nc := 0 for !ia.Done() { nc = nc + 1 ia.Next() } fmt.Printf("Number of chars: %d\n", nc) }
ここでは、 Unicode正規化フォーム NFKD "互換性分解"
OLIVER 'S 答え 唯一の方法として特定の重要なテキスト要素間のデフォルトの境界を確実に決定します。ユーザー認知された文字、単語、文。
そのためには、 rivo / uniseg のような外部ライブラリが必要です。 Unicodeテキストセグメンテーション。
実際には " grapheme cluster < / strong> "。複数のコードポイントを1つのユーザー認識文字にまとめることができます。
.package uniseg import ( "fmt" "github.com/rivo/uniseg" ) func main() { gr := uniseg.NewGraphemes("👍🏼!") for gr.Next() { fmt.Printf("%x ", gr.Runes()) } // Output: [1f44d 1f3fc] [21] }
2つのグレイデミスは、3つのルーン(Unicode Code Points)があります。
他のヒント
Stringを[] Runeにlen([]rune(YOUR_STRING))
:として変換することによって、パッケージなしでルーンの数を取得する方法があります。
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
は、「文字」が何であるかの定義に大きく依存します。「Runeが文字に等しい」があなたの仕事のために問題ない場合(一般的にはそうではありません)、それからvoncによる答えはあなたに最適です。それ以外の場合は、Unicode文字列内の実行数が興味深い値である状況がいくつかあると注意すべきです。そしてそれらの状況でさえも、可能であれば、可能であれば、弦を「横断する」と弦を「横断する」と推測されると、UTF-8デコード努力を倍増させないように処理されます。
Grapheme Clustersを考慮する必要がある場合は、regexpまたはUnicodeモジュールを使用してください。GrapheMeクラスタの長さが無制限であるため、validaitonにはコードポイント数(runes)またはバイト数も必要です。非常に長いシーケンスを排除したい場合は、シーケンスがストリームセーフなテキスト形式に準拠しているかどうかを確認してください。a>。
.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)
}
私はこれまでに提供された答えのどれもあなたが期待されるようにあなたに与えられた答えのどれも、特にあなたがEmojisを扱うとき(しかし、タイ、韓国語、またはアラビア語のようないくつかの言語)をあなたに与えることを指摘するべきです。 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文字列を(ユーザー認識された)文字、すなわちGraphemeクラスタに分割する正しい方法は、 Unicode Standard Annex#29 。規則は、セクション3.1.1 セクション3.1.1で見つけることができます。 github.com/rivo/uniseg パッケージはこれらの規則を実装しているので、正しい文字数を決定できます。文字列:
fmt.Println(uniseg.GraphemeClusterCount("🏳️🌈🇩🇪")) // Outputs "2".
. 文字列の長さを取得する方法はいくつかあります:
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)
}
. 正規化を少し高速にするために作りようとしました:
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
}
.