扫码一下
查看教程更方便
在基础篇 go 语言字符串详解 一章中,我们介绍过在go语言中访问字符串的单个字符的方法。在列举的示例中有一个问题,就是如果一个字符占多个字节的话,例如:中文,在访问单个字符的时候就会出现问题。我们看下面的代码
package main
import (
"fmt"
)
func printbytes(s string) {
fmt.printf("bytes: ")
for i := 0; i < len(s); i {
fmt.printf("%x ", s[i])
}
}
func printchars(s string) {
fmt.printf("characters: ")
for i := 0; i < len(s); i {
fmt.printf("%c ", s[i])
}
}
func main() {
name := "hello world"
fmt.printf("string: %s\n", name)
printchars(name)
fmt.printf("\n")
printbytes(name)
fmt.printf("\n\n")
name = "迹忆客"
fmt.printf("string: %s\n", name)
printchars(name)
fmt.printf("\n")
printbytes(name)
}
上述代码执行结果如下
string: hello world
characters: h e l l o w o r l d
bytes: 48 65 6c 6c 6f 20 57 6f 72 6c 64
string: 迹忆客
characters: è ¿ ¹ å ¿ å ® ¢
bytes: e8 bf b9 e5 bf 86 e5 ae a2
在上面程序中我们试图打印迹忆客
的字符。但是出人意料的它打印出了è ¿ ¹ å ¿ å ® ¢
。为什么会出现这种情况呢,原因就是utf-8 编码的中文它不是只占一个字节。因此我们试图打印字符,假设每个代码点都是一个字节长,这是错误的。
在 utf-8 编码中,一个代码点可以占用 1 个以上的字节。
那我们如何解决这个问题呢,那就需要用到 rune
这个万能钥匙了。
rune 是go 中的内置类型,它是 int32 的别名。rune 代表 go 中的 unicode 代码点。代码点占用多少字节并不重要,可以用一个符文来表示。
下面让我们修改上面的代码
package main
import (
"fmt"
)
func printbytes(s string) {
fmt.printf("bytes: ")
for i := 0; i < len(s); i {
fmt.printf("%x ", s[i])
}
}
func printchars(s string) {
fmt.printf("characters: ")
runes := []rune(s)
for i := 0; i < len(runes); i {
fmt.printf("%c ", runes[i])
}
}
func main() {
name := "hello world"
fmt.printf("string: %s\n", name)
printchars(name)
fmt.printf("\n")
printbytes(name)
fmt.printf("\n\n")
name = "迹忆客"
fmt.printf("string: %s\n", name)
printchars(name)
fmt.printf("\n")
printbytes(name)
fmt.printf("\n")
}
这样上面的代码执行结果如下
string: hello world
characters: h e l l o w o r l d
bytes: 48 65 6c 6c 6f 20 57 6f 72 6c 64
string: 迹忆客
characters: 迹 忆 客
bytes: e8 bf b9 e5 bf 86 e5 ae a2
上面代码中字符串被转换为一个符文切片。然后我们对它进行循环显示。可以看到,中文的字符已经被正常打印出来了。
上面的程序是迭代字符串的各个符文的完美方式。但是使用go语言的 for range
将更加容易的实现上面的功能。我们看下面的代码
package main
import (
"fmt"
)
func charsandbyteposition(s string) {
for index, rune := range s {
fmt.printf("%c 从字节 %d 处开始\n", rune, index)
}
}
func main() {
name := "迹忆客"
charsandbyteposition(name)
}
上面代码执行结果如下
迹 从字节 0 处开始
忆 从字节 3 处开始
客 从字节 6 处开始
从上面的输出中可以很明显的看出,在utf8编码下,每个中文字符占 3 个字节
我们直接看示例
package main
import (
"fmt"
)
func main() {
runeslice := []rune{0x8ff9, 0x5fc6, 0x5ba2}
str := string(runeslice)
fmt.println(str)
}
上面代码输出结果如下
迹忆客
在上面的程序中 runeslice 包含 迹忆客
十六进制字符串的unicode代码点。