扫码一下
查看教程更方便
在基础篇 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代码点。