如何在Go中获取字符串的字符数?
例如,如果我有一个字符串,则"hello"
该方法应返回5
。我看到len(str)
返回的字节数,而不是字符的数量,以便len("£")
返回2而不是1,因为£被编码有在UTF-8的两个字节。
如何在Go中获取字符串的字符数?
例如,如果我有一个字符串,则"hello"
该方法应返回5
。我看到len(str)
返回的字节数,而不是字符的数量,以便len("£")
返回2而不是1,因为£被编码有在UTF-8的两个字节。
Answers:
您可以RuneCountInString
从utf8包中尝试。
返回p中的符文数
如该脚本所示:“ World”的长度可能是6(当用中文写成“ World”时),但其符文数为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调用。
添加一个新的运行时函数以计算字符串中的符文。修改编译器以检测模式,
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)
Stefan Steiger指向博客文章“ Go中的文本规范化 ”
什么是角色?
正如字符串博客文章中提到的那样,字符可以跨越多个符文。
例如,一个'e
'和'◌́◌́'(急性“ \ u0301”)可以组合成一个'é'(e\u0301
在NFD中为“ ”)。这两个符文合在一起是一个角色。
字符的定义可能会因应用程序而异。
为了规范化,我们将其定义为:
- 以起动器开始的一系列符文,
归一化算法一次处理一个字符。
使用该程序包及其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是可靠确定某些重要文本元素(用户感知的字符,单词和句子)之间默认边界的唯一方法。
为此,您需要一个像rivo / uniseg这样的外部库,它可以执行Unicode文本分段。
将实际计数“ 字形簇 ”,其中多个码点可被组合成一个用户感知的字符。
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]
}
即使有三个符文(Unicode代码点),也有两个字素。
您可以在“ 如何在GO中操作字符串以反转它们? ”中看到其他示例。
🦰🦰单独是一个字素,但是,从unicode到代码点转换器,有4种符文:
有一种方法可以通过将字符串转换为[] 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
在很大程度上取决于您对“字符”的定义。如果“符文等于字符”对您的任务是可以的(通常不是),那么VonC的答案将非常适合您。否则,应该指出的是,在少数情况下,Unicode字符串中的符文数是一个有趣的值。而且即使在那些情况下,最好还是在处理符文时“遍历”字符串时推断计数,以避免加倍UTF-8解码工作。
String
的.length()
方法不返回字符数任。也不对可可的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代码点。有许多字符可以由多个代码点组成。
与使用Normalization包相同:
var ia norm.Iter
ia.InitString(norm.NFKD, "🏳️🌈🇩🇪")
nc := 0
for !ia.Done() {
nc = nc + 1
ia.Next()
}
fmt.Println(nc) // Outputs "6".
规范化与计数字符实际上并不相同,并且许多字符无法规范化为一个代码点等效项。
masakielastic的答案很接近,但只能处理修饰符(rainbow标志包含修饰符,因此该修饰符不算作自己的代码点):
fmt.Println(GraphemeCountInString("🏳️🌈🇩🇪")) // Outputs "5".
fmt.Println(GraphemeCountInString2("🏳️🌈🇩🇪")) // Outputs "5".
Unicode标准附件#29中定义了将Unicode字符串拆分为(用户可感知的)字符(即字形簇)的正确方法。这些规则可以在第3.1.1节中找到。该github.com/rivo/uniseg包实现这些规则,因此可以判断字符串中的字符的正确数量:
fmt.Println(uniseg.GraphemeClusterCount("🏳️🌈🇩🇪")) // Outputs "2".