Goで文字列の文字数を取得するにはどうすればよいですか?
たとえば、文字列がある場合"hello"
、メソッドはを返し5
ます。私はそれを見たlen(str)
リターンをバイト数としないので、文字の数をlen("£")
£は、UTF-8で2バイトで符号化されるので、リターン2の代わりに、1。
Goで文字列の文字数を取得するにはどうすればよいですか?
たとえば、文字列がある場合"hello"
、メソッドはを返し5
ます。私はそれを見たlen(str)
リターンをバイト数としないので、文字の数をlen("£")
£は、UTF-8で2バイトで符号化されるので、リターン2の代わりに、1。
回答:
RuneCountInString
utf8パッケージから試すことができます。
ルーンの数をpで返します
このスクリプトに示されているように、「ワールド」の長さは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:= range s呼び出しに置き換えます。
文字列内のルーンをカウントする新しいランタイム関数を追加します。コンパイラを変更してパターンを検出
len([]rune(string))
し、新しいルーンカウントランタイム関数に置き換えます。
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)
ステファン・スタイガーのブログ記事へのポイント「ゴーでのテキストの正規化」
キャラクターとは?
文字列のブログ投稿で述べたように、文字は複数のルーンにまたがることができます。
たとえば、 'e
'と '◌́◌́'(acute "\ u0301")を組み合わせて 'é'(e\u0301
NFD では" ")を作成できます。これらの2つのルーンは一緒に1つのキャラクターです。
文字の定義は、アプリケーションによって異なる場合があります。
以下のために正規我々はそれをとして定義されます:
- スターターで始まる一連のルーン
正規化アルゴリズムは、一度に1文字を処理します。
そのパッケージとそのIter
タイプを使用すると、「文字」の実際の数は次のようになります。
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の回答は、特定の重要なテキスト要素(ユーザーが認識する文字、単語、および文章)間のデフォルトの境界を確実に決定する唯一の方法として、UNICODE TEXT SEGMENTATIONを指しています。
そのためには、Unicodeテキストのセグメンテーションを行うrivo / unisegのような外部ライブラリが必要です。
これは、実際には「書記素クラスタ」を数えます。この場合、複数のコードポイントが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]
}
3つのルーン(Unicodeコードポイント)があるにもかかわらず、2つの書記素。
あなたは「内の他の例を見ることができ、それらを逆にGOで文字列を操作する方法は?」
👩🏾🦰は1つの書記素ですが、Unicodeからコードポイントコンバーターへの変換では、4つのルーン文字:
文字列を[] rune asに変換することにより、パッケージなしでルーンの数を取得する方法があります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
「キャラクター」が何であるかのあなたの定義に大きく依存します。「ルーンが文字に等しい」があなたのタスクに適している場合(通常はそうではありません)、VonCによる回答が最適です。それ以外の場合、Unicode文字列のルーンの数が興味深い値である状況はほとんどないことに注意してください。そして、これらの状況でも、可能であれば、ルーンが処理されるときに文字列を「トラバース」しながらカウントを推測して、UTF-8デコードの作業が2倍になるのを回避することをお勧めします。
String
の.length()
メソッドは文字数も返しません。どちらもCocoaの行いませんNSString
の-length
方法。それらは単にUTF-16エンティティの数を返します。ただし、コードポイントのカウントに線形時間がかかるため、コードポイントの実際の数はほとんど使用されません。
書記素クラスタを考慮する必要がある場合は、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)
}
var
、関数の外部として抽出する必要があります。
文字列の長さを取得するには、いくつかの方法があります。
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)
}
特に絵文字(ただし、タイ語、韓国語、アラビア語などの一部の言語)を扱っている場合は、これまでに提供されたどの回答も期待どおりの文字数を提供しないことに注意してください。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の答えは近づきますが、修飾子のみを処理します(虹色の旗には修飾子が含まれているため、独自のコードポイントとしてはカウントされません)。
fmt.Println(GraphemeCountInString("🏳️🌈🇩🇪")) // Outputs "5".
fmt.Println(GraphemeCountInString2("🏳️🌈🇩🇪")) // Outputs "5".
Unicode文字列を(ユーザーが認識する)文字、つまり書記素クラスタに分割する正しい方法は、Unicode Standard Annex#29で定義されています。ルールはセクション3.1.1にあります。github.com/rivo/unisegのあなたは、文字列内の文字の正しい数を決定することができますので、パッケージには、これらの規則を実装します。
fmt.Println(uniseg.GraphemeClusterCount("🏳️🌈🇩🇪")) // Outputs "2".