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

新手上路,请多包涵

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

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

原文由 Ammar 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 971
2 个回答

您可以从 utf8 包中尝试 RuneCountInString

返回 p 中的符文数

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

 package main

import "fmt"
import "unicode/utf8"

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

Phrozen 在评论中 补充道:

其实你可以做 len() over runes 通过类型转换。

len([]rune("世界")) 将打印 2 。至少在 Go 1.3 中。


对于 CL 108985 (2018 年 5 月,对于 Go 1.11), len([]rune(string)) 现已优化。 (修复 问题 24923

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

添加一个新的运行时函数来计算字符串中的符文。修改编译器以检测模式 len([]rune(string)) 并将其替换为新的符文计数运行时函数。

>  RuneCount/lenruneslice/ASCII        27.8ns ± 2%  14.5ns ± 3%  -47.70%
> RuneCount/lenruneslice/Japanese     126ns ± 2%   60  ns ± 2%  -52.03%
> RuneCount/lenruneslice/MixedLength  104ns ± 2%   50  ns ± 1%  -51.71%
>
> ```

* * *

[Stefan Steiger](https://stackoverflow.com/users/155077/stefan-steiger) 指向博客文章“ [Go 中的文本规范化](https://blog.golang.org/normalization)”

> ## 什么是角色?

> 正如 [字符串博客文章](http://blog.golang.org/strings) 中提到的, **字符可以跨越多个符文**。
>
> 例如,“ `e` ”和“◌́◌́”(尖音符号“\\u0301”)可以组合形成“é”(NFD 中的“ `e\u0301` ”)。 **这两个符文合为一个字**。
>
> 字符的定义可能因应用程序而异。
>
> 对于 **[规范化](https://godoc.org/golang.org/x/text/unicode/norm)**,我们将其定义为:
>
> - 以启动符开头的一系列符文,
> - 一个不修改或与任何其他符文向后组合的符文,
> - 后跟可能为空的非首发序列,即符文(通常是重音符号)。
>
> 规范化算法一次处理一个字符。

使用该包及其 [`Iter` type](https://godoc.org/golang.org/x/text/unicode/norm#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 规范化形式](http://unicode.org/reports/tr15/#Norm_Forms) NFKD“兼容性分解”

* * *

[Oliver](https://stackoverflow.com/users/2046109/oliver) 的 [回答](https://stackoverflow.com/a/55151396/6309) 指出 **[UNICODE TEXT SEGMENTATION](http://unicode.org/reports/tr29/)** 是可靠地确定某些重要文本元素之间默认边界的唯一方法:用户感知的字符、单词和句子。

为此,您需要一个外部库,如 [**rivo/uniseg**](https://github.com/rivo/uniseg) ,它执行 **Unicode 文本分段**。

这实际上会计算“ _**[字素](https://en.wikipedia.org/wiki/Grapheme) 簇**_”,其中多个代码点可能会组合成一个用户感知的字符。

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 到代码点转换器,4 个符文:

原文由 VonC 发布,翻译遵循 CC BY-SA 4.0 许可协议

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

原文由 Denis Kreshikhin 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题