0.1、索引https://waterflow.link/articles/1666449874974
1、字符串编码在go中rune是一个unicode编码点 。
我们都知道UTF-8将字符编码为1-4个字节 , 比如我们常用的汉字,UTF-8编码为3个字节 。所以rune也是int32的别名 。
type rune = int32
当我们打印一个英文字符hello的时候,我们可以得到s的长度为5,因为英文字母代表1个字节:
package mainimport "fmt"func main() { s := "hello" fmt.Println(len(s)) // 5}
但是当我们打印嗨
的时候,会打印3个字节 。因为使用UTF-8,这个字符会被编码成3个字节:
package mainimport "fmt"func main() { s := "嗨" fmt.Println(len(s)) // 3}
所以,我们使用len内置函数输出的并不是字符数,而是字节数 。
下面看一个有趣的例子,我们都知道汉字符使用3个字节编码,分别是0xE5, 0x97, 0xA8 。我们运行下面代码会得到汉字嗨
:
package mainimport "fmt"func main() { s := string([]byte{0xE5, 0x97, 0xA8}) fmt.Println(s) // 嗨}
所以我们需要知道:
- 字符集是一组字符,而编码描述了如何将字符集转换为二进制
- 在 Go 中,字符串引用任意字节的不可变切片
- Go 源码使用 UTF-8 编码 。因此,所有字符串文字都是 UTF-8 字符串 。但是因为字符串可以包含任意字节,如果它是从其他地方(不是源码)获得的,则不能保证它是基于 UTF-8 编码的
- 使用 UTF-8 , 一个 Unicode 字符可以编码为 1 到 4 个字节
- 在 Go 中对字符串使用 len 返回字节数,而不是字符数
我们看下下面的例子,打印一个字符串中的不同字符和对应的位置:
package mainimport "fmt"func main() { s := "h嗨llo" for i := range s {fmt.Printf("字符位置 %d: %c\n", i, s[i]) } fmt.Printf("len=%d\n", len(s))}
go run 7.go字符位置 0: h字符位置 1: ?字符位置 4: l字符位置 5: l字符位置 6: olen=7
我们想要的效果是通过遍历字符串 , 打印出每个字符的索引 。但是我们却得到了一个特殊的字符?
,其实我们想要的是嗨
。但是打印的字节数是符合我们的预期的,因为
嗨
是一个中文占用了3个字节,所以len返回的是7 。3、字符串中的字符数如果我们想要正确的获取字符串的字符数,可以使用go中的utf8包:
package mainimport ( "fmt" "unicode/utf8")func main() { s := "h嗨llo" for i := range s {fmt.Printf("字符位置 %d: %c\n", i, s[i]) } fmt.Printf("len=%d\n", len(s)) fmt.Printf(" rune len=%d\n", utf8.RuneCountInString(s)) // 获取字符数}
go run 7.go字符位置 0: h字符位置 1: ?字符位置 4: l字符位置 5: l字符位置 6: olen=7 rune len=5
在这个例子中,可以看到,我们确实遍历了5次,也就是对应字符串的5个字符 。但是我们获取到的索引其实是对应每个字符的起始位置 。像下面这样
文章插图
那我们如何打印出正确的结果呢?我们稍微修改下代码:
package mainimport ( "fmt" "unicode/utf8")func main() { s := "h嗨llo" for i, v := range s { // 此处改为获取v , 可以获取到字符本身fmt.Printf("字符位置 %d: %c\n", i, v) } fmt.Printf("len=%d\n", len(s)) fmt.Printf(" rune len=%d\n", utf8.RuneCountInString(s))}
go run 7.go字符位置 0: h字符位置 1: 嗨字符位置 4: l字符位置 5: l字符位置 6: olen=7 rune len=5
另外一种方法就是把字符串转换成rune切片,这样也会正确打印结果:package mainimport ( "fmt" "unicode/utf8")func main() { s := "h嗨llo" b := []rune(s) for i := range b {fmt.Printf("字符位置 %d: %c\n", i, b[i]) } fmt.Printf("len=%d\n", len(s)) fmt.Printf(" rune len=%d\n", utf8.RuneCountInString(s))}
go run 7.go字符位置 0: h字符位置 1: 嗨字符位置 2: l字符位置 3: l字符位置 4: olen=7 rune len=5
下面是rune切片遍历的过程(中间省略了将字节转换为rune的过程,需要遍历字节,复杂度为O(n))
文章插图
4、字符串trim开发中我们经常会遇到去除字符串头部或者尾部字符的操作 。比如我们现在有个字符串
xohelloxo
,现在我们想去除尾部的
推荐阅读
- DevOps|从特拉斯辞职风波到研发效能中的不靠谱人干的荒唐事
- 月圆之夜镜中的记忆全成就攻略是什么
- 怎样转发微信内容到朋友圈(如何将微信中的内容转发到朋友圈)
- 4 Java I/O:AIO和NIO中的Selector
- 时空中的绘旅人诸界归一出行服装羽翼获取方法是什么
- 时空中的绘旅人服装的获取方法是什么
- 划拳中的十五、二十怎么玩啊(划拳必赢的十大技巧)
- Vue3 SFC 和 TSX 方式调用子组件中的函数
- 3 Java I/O:NIO中的Buffer
- 我的Vue之旅 07 Axios + Golang + Sqlite3 实现简单评论机制