ChatGPT解决这个技术问题 Extra ChatGPT

如何获取字符串中的字符数

如何在 Go 中获取字符串的字符数?

例如,如果我有一个字符串 "hello",则该方法应该返回 5。我看到 len(str) 返回字节数而不是字符数,因此 len("£") 返回 2 而不是 1,因为 £ 在 UTF-8 中用两个字节编码。


b
bit

您可以尝试使用 utf8 包中的 RuneCountInString

返回 p 中的符文数

this script所示:“世界”的长度可能是6(中文写成:“世界”),但“世界”的符文计数是2:

package main
    
import "fmt"
import "unicode/utf8"
    
func main() {
    fmt.Println("Hello, 世界", len("世界"), utf8.RuneCountInString("世界"))
}

Phrozen 添加 in the comments

实际上,您只需键入强制转换即可对符文执行 len()
len([]rune("世界")) 将打印 2。至少在 Go 1.3 中。

借助 CL 108985(2018 年 5 月,针对 Go 1.11),现在对 len([]rune(string)) 进行了优化。 (修复 issue 24923

编译器自动检测 len([]rune(string)) 模式,并将其替换为 for r := range s 调用。

添加一个新的运行时函数来计算字符串中的符文。修改编译器以检测模式 len([]rune(string)) 并将其替换为新的 rune 计数运行时函数。 RuneCount/lenruneslice/ASCII 27.8ns ± 2% 14.5ns ± 3% -47.70% RuneCount/lenruneslice/日语 126ns ± 2% 60 ns ± 2% -52.03% RuneCount/lenruneslice/MixedLength 104ns ± 2% 50 ns ± 1% - 51.71%

Stefan Steiger 指向博文“Text normalization in Go

什么是性格?

正如字符串博客文章中提到的,字符可以跨越多个符文。例如,“e”和“◌́◌́”(急性“\u0301”)可以组合形成“é”(NFD 中的“e\u0301”)。这两个符文合起来就是一个字符。字符的定义可能因应用而异。为了规范化,我们将其定义为:以启动器开头的符文序列,不修改或与任何其他符文向后组合的符文,然后是可能为空的非启动符序列,即可以(通常口音)。规范化算法一次处理一个字符。

使用该包及其 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 Normalization form NFKD“兼容性分解”

Oliveranswer 指出 UNICODE TEXT SEGMENTATION 是可靠确定某些重要文本元素(用户感知的字符、单词和句子)之间默认边界的唯一方法。

为此,您需要一个像 rivo/uniseg 这样的外部库,它执行 Unicode 文本分割

这实际上将计入“grapheme cluster”,其中多个代码点可以组合成一个用户感知的字符。

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 代码点)。

您可以在“How to manipulate strings in GO to reverse them?”中查看其他示例

👩🏾‍🦰 单独是一个字素,但是,从 unicode to code points converter 开始,有 4 个符文:

👩: 女性 (1f469)

黑皮肤(1f3fe)

零宽度连接器 (200d)

🦰红头发 (1f9b0)


您可以在 stackoverflow.com/a/1758098/6309 的这个字符串还原函数中看到它的实际效果
这只会告诉你符文的数量,而不是字形的数量。许多字形由多个符文组成。
实际上,您只需键入强制转换就可以在符文上执行 len() ...... len([]rune("世界")) 将打印 2。至少在 Go 1.3 中,不知道它有多久了。
@VonC:实际上,一个字符(字形的口语术语)可以 - 偶尔 - 跨越几个符文,所以这个答案是,使用精确的技术术语,错误。您需要的是 Grapheme/GraphemeCluster 计数,而不是符文计数。例如,一个 'e' 和 '◌́' (acute "\u0301") 可以组合成 'é'(NFD 中的 "e\u0301")。但是人类会(正确地)看待 é作为一个角色..显然它在泰卢固语中有所作为。但也可能是法语,具体取决于您使用的键盘/语言环境。 blog.golang.org/normalization
@juancortez 正如 blog.golang.org/strings 中所解释的,字符串只是字节的一部分:它包含任意字节。不需要保存 Unicode 文本、UTF-8 文本或任何其他预定义格式。没什么特别的”。 golang.org/pkg/unicode/utf8 允许将字符串文字解释为符文的集合。这不足以可靠地确定一个字符。因此需要 Unicode 文本分割第三方库,以可靠地确定字符串中的实际字形/字形。
D
Denis Kreshikhin

有一种方法可以通过将字符串转换为 []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


O
Oliver

我应该指出,到目前为止提供的答案都没有像您期望的那样为您提供字符数,尤其是当您处理表情符号时(还有一些语言,如泰语、韩语或阿拉伯语)。 VonC's suggestions 将输出以下内容:

fmt.Println(utf8.RuneCountInString("🏳️‍🌈🇩🇪")) // Outputs "6".
fmt.Println(len([]rune("🏳️‍🌈🇩🇪"))) // Outputs "6".

那是因为这些方法只计算 Unicode 代码点。有许多字符可以由多个代码点组成。

与使用 Normalization package 相同:

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

规范化实际上与计数字符不同,许多字符不能规范化为一个代码点等价物。

masakielastic's answer 接近但只处理修饰符(彩虹标志包含一个修饰符,因此不计入其自己的代码点):

fmt.Println(GraphemeCountInString("🏳️‍🌈🇩🇪"))  // Outputs "5".
fmt.Println(GraphemeCountInString2("🏳️‍🌈🇩🇪")) // Outputs "5".

Unicode Standard Annex #29 中定义了将 Unicode 字符串拆分为(用户感知的)字符(即字素簇)的正确方法。规则可以在 Section 3.1.1 中找到。 github.com/rivo/uniseg 包实现了这些规则,因此您可以确定字符串中正确的字符数:

fmt.Println(uniseg.GraphemeClusterCount("🏳️‍🌈🇩🇪")) // Outputs "2".

m
masakielastic

如果您需要考虑字素簇,请使用 regexp 或 unicode 模块。由于字素簇的长度是无限的,因此验证也需要计算代码点(符文)或字节的数量。如果要消除极长的序列,请检查序列是否符合 stream-safe text format

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
p
pigletfly

有几种方法可以获取字符串长度:

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)

}


z
zzzz

很大程度上取决于您对“角色”的定义。如果“符文等于一个字符”对您的任务来说是可以的(通常不是),那么 VonC 的答案对您来说是完美的。否则,应该注意的是,在少数情况下,Unicode 字符串中的符文数量是一个有趣的值。即使在这些情况下,如果可能的话,最好在处理符文时在“遍历”字符串时推断计数,以避免将 UTF-8 解码工作加倍。


您什么时候会将符文视为角色? Go 规范将符文定义为 Unicode 代码点:golang.org/ref/spec#Rune_literals
另外,为了避免使解码工作加倍,我只做一个 []rune(str),处理它,然后在我完成后转换回字符串。我认为这比遍历字符串时跟踪代码点更容易。
@ThomasKappler:什么时候?好吧,当 rune 不是一个字符时,它通常不是。只有一些符文与字符相等,而不是全部。假设“rune == character”仅对 Unicode 字符的子集有效。示例:en.wikipedia.org/wiki/…
@ThomasKappler:但是如果您这样看,那么例如Java 的String.length() 方法也不会返回字符数。 Cocoa 的 NSString-length 方法也没有。那些只是返回 UTF-16 实体的数量。但是很少使用代码点的真实数量,因为计算它需要线性时间。
M
Marcelloh

我试图更快地进行标准化:

    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
    }