前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Go语言中常见100问题-#37 Inaccurate string iteration

Go语言中常见100问题-#37 Inaccurate string iteration

作者头像
数据小冰
发布2023-08-17 08:32:55
1470
发布2023-08-17 08:32:55
举报
文章被收录于专栏:数据小冰数据小冰
字符串迭代误区

对字符串进行迭代是一个非常常见的操作,例如我们想要对字符串中的每个rune做一些操作或者实现一个查找具体子串的函数。但是字符串迭代中存在一些误区,需要了解掌握,避免踩坑。

来看一个具体的例子,我们想打印字符串中每个rune字符以及它所在的位置,实现代码如下。

代码语言:javascript
复制
s := "hêllo"
for i := range s {
    fmt.Printf("position %d: %c\n", i, s[i])
}
fmt.Printf("len=%d\n", len(s))

通过range迭代字符串s,输出每个rune的位置和内容如下。

代码语言:javascript
复制
position 0: h
position 1: Ã
position 3: l
position 4: l
position 5: o
len=6

啥?输出是这样的吗,怎么与我们预期的不一致。主要在以下三个方面:

  • 第二个rune是Ã而不是字符串中的ê
  • 输出的rune位置直接从1跳到了3,没有位置2
  • len输出字符串长度是6,但是打印的只有5个字符

len输出的值为啥是6而不是5呢?因为len返回的是字符串中byte的数量,而不是字符的个数。将字符串赋值给s后,s被编码为UTF-8。字符ê不是一个简单字符,编码后占用2个字节而不是1个字节。

那如果我们想要统计一个字符串中字符的个数而不是byte数量,怎么办呢?处理方法依赖于字符串采用的编码,像本文中字符串s采用的是UTF-8编码,可以使用 unicode/utf8 包提供的统计字符串数量函数。

代码语言:javascript
复制
fmt.Println(utf8.RuneCountInString(s)) // 5

现在回头来分析上述迭代输出内容为啥是这样的原因。如下图所示,打印s[i]的值并不是打印第i个rune的内容,而是从索引位置i开始UTF-8编码后的内容,所以输出的是hÃllo而不是hêllo。

代码语言:javascript
复制
for i := range s {
    fmt.Printf("position %d: %c\n", i, s[i])
}

如果我们想原样输出每个字符,有两种方法。方法一是采用range返回的每个字符,实现代码如下。与最初版本实现的不同点是,打印变量r的值而不是s[i]. range作用于字符串时会返回两个值,值1是rune的下标索引,值2是rune本身。

代码语言:javascript
复制
s := "hêllo"
for i, r := range s {
    fmt.Printf("position %d: %c\n", i, r)
}

上述程序输出结果如下:

代码语言:javascript
复制
position 0: h
position 1: ê
position 3: l
position 4: l
position 5: o 

方法二是将字符串转成rune切片,然后遍历rune切片,实现代码如下。相比最初版本,这里直接打印每个rune索引。

代码语言:javascript
复制
s := "hêllo"
runes := []rune(s)
for i, r := range runes {
    fmt.Printf("position %d: %c\n", i, r)
}
position 0: h
position 1: ê
position 2: l
position 3: l
position 4: o

相比方法一,方法二引入了运行时开销,将字符串转为rune切片需要额外分配内存,将byte转为rune也有O(n)的时间复杂度开销(n为字符串长度)。因此,如果我们想迭代全部的rune,推荐使用方法一。

但是,如果我们想通过下标索引访问字符串中第i个rune字符,此时我们并不知道它准确的起始位置,例如在hêllo中,第一个 l 字符的起始位置是3而不是2. 相反,通过字符串的rune切片,直接访问切片中的第i个值便是我们想要获取的第i个rune字符,这种情况下,我们应该选用方法二。

下面的代码直接输出字符串s中第5个字符。

代码语言:javascript
复制
s := "hêllo"
r := []rune(s)[4]
fmt.Printf("%c\n", r) // o

「NOTE: 访问字符串中某个字符优化方法:如果字符串中所有的字符都是单字节的,例如,字符串中所有的字符都在A-Z或a-z,我们可以直接访问第i个字符,而不用先将字符串转为rune切片。实例代码如下。」

代码语言:javascript
复制
s := "hello"
fmt.Printf("%c\n", rune(s[4])) // o
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-06-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 数据小冰 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 字符串迭代误区
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档