Go 语言中的 byte 类型和 rune 类型
Go 语言中定义字符串要用双引号,而不是单引号,单引号中只能包含一个元素,表示一个字符。
Go语言中字符有两种类型,分别是:
uint8
类型,或者叫byte
型,代表了 ASCII 码的一个字符rune
类型,代表一个 UTF-8 字符
了解编码知识的应该知道,ASCII 编码字符是1个字节的,而 UTF-8 是可变长的编码,当要表示中文等非 ASCll 编码的字符时,需要使用 UTF-8 编码来保证不会乱码。关于字符编码相关知识,推荐看这篇廖雪峰的介绍 字符编码
假如我们要遍历输出一个包含中文的字符串时:
package main
import "fmt"
func main() {
str := "hello 世界"
for i := 0; i < len(str); i++ {
fmt.Println(str[i])
}
}
上述代码的打印结果是:
104
101
108
108
111
32
228
184
150
231
149
140
Go 语言字符串存储的其实是类型为 byte 的只读切片,或者说一个字符串就是一堆字节。在 UTF-8 编码中一个英文字符可以用一个字节存储,一个中文字符需要三个或四个字节存储,而 ASCII 码符号只有 128 个,大于 128 的都不在范围内。上述代码的遍历方式就是以 ASCII 类型来读字符的。
可以看到输出结果中的前 6 个都是在 ASCII 表中的,但是后 6 个就不在表中了,后面 6 个的每一个字节都不能单独表示一个字符,而是用 3 个在一起才能表示一个字符,明显超出了 ASCII 表的范围,所以当打印具体字符时就会乱码,比如我们现在来打印每一个真实字符,需要使用到占位符 %c
:
package main
import "fmt"
func main() {
str := "hello 世界"
for i := 0; i < len(str); i++ {
fmt.Printf("%c", str[i]) // hello ä¸ç
}
}
输出结果为:
hello ä¸ç
明显乱码了。解释这么多就是为了说明如果字符串中有非 ASCII 码的字符时,就不能使用 byte
来表示字符,需要使用 rune
类型来表示。
使用 rune 类型来遍历字符串
在 Go 中,有一个遍历方式是 range
,它默认就是以 UTF-8 编码形式去读每一个字符。当涉及到的字符串中含有非英文字符时,可以使用 range
来遍历:
package main
import "fmt"
func main() {
str := "hello 世界"
for _, r := range str {
fmt.Println(r)
}
}
输出结果为:
104
101
108
108
111
32
19990
30028
此时输出的字节编码就是 UTF-8 编码号,UTF-8 编码是包含 ASCII 编码的,所以前 6 个编号还是一样的,后面两个编号分别代表世
,界
。
修改字符串
Go 语言中对字符串的修改其实不是对字符串本身的修改,而是复制字符串,同时修改值,即重新分配来内存,需要先将字符串转化成数组,[]byte
或 []rune
,然后再转换成 string 型。
那么我要说的也很明显了,就是要区别使用 []byte
或 []rune
。
对于全是ASCII编码的字符串,使用 []byte
即可:
package main
import "fmt"
func main() {
str := "abc"
s2 := []byte(str)
s2[0] = 'b'
fmt.Println(string(s2)) //bbc
}
// string()表示强制类型转换,转换为字符串
对于包含中文等字符的字符串时,那就要用 []rune
了:
func main() {
str:="白猫"
s2:=[]rune(str)
s2[0]='黑'
fmt.Println(string(s2)) //黑猫
}
总结
在处理字符时,要考虑字符的编码范围,然后根据需要使用 byte
类型或 rune
类型。
byte
类型只能正常输出 ASCII 编码范围的字符;rune
类型可以输出 UTF-8 编码范围的字符。