Go 语言中的 byte 类型和 rune 类型

Go 语言中定义字符串要用双引号,而不是单引号,单引号中只能包含一个元素,表示一个字符。

Go语言中字符有两种类型,分别是:

  1. uint8 类型,或者叫 byte 型,代表了 ASCII 码的一个字符
  2. 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 编码范围的字符。

posted @ 2021-05-28 16:46  黑色瓶子  阅读(1609)  评论(0编辑  收藏  举报