字符集

字符集是数字与字符对应关系的集合,像 ASCII、Unicode 都是属于字符集,声明了哪个字符对应哪个特定的数字

编码

广义上的编码就是将一个对象转换为一段字节序列,或者将一段字节序列转换为另一段字节序列,解码就是将一段字节序列恢复为一个对象,或者恢复为另一段字节序列。

字符编码

用于字符的编码,其实就是针对字符集中数字的一种编码。编码的过程就是将数字转换成相应的一段字节序列的过程。不同的字符编码,对同一个数字编码出的字节序列是不同的。所以用一种编码去恢复另一种编码的字节序列,会得到不同的数字,也就对应不同的字符,也有可能直接解码失败,拿不到数字。这就是常见的乱码原因。

utf-8 就是一种针对 Unicode 字符集中所有数字的一种编码。类似的还有 utf-16、gb2312。

Go 中的字符编码

Go 语言内部默认的字符编码为 utf-8。这意味着一个默认的 string 类型的字符串,在转换为字节数组时,拿到的是 utf-8 编码之后的字节序列。当然我们也指定其他编码方式来获取对应编码的字节序列。在使用 string() 直接将一段字节序列转为 string 时,默认也是用的 utf-8 。如果该段字节序列不是 utf-8 编码的则会出现乱码。

执行如下代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import (
	"encoding/hex"
	"fmt"
)

func main() {
	name := "我爱你"                           //字符串
	byteSlice := []byte(name)               //默认按照 utf-8 对字符串编码
	fmt.Printf("utf-8 编码: %v\n", byteSlice) //输出编码后的字节 [230 136 145 231 136 177 228 189 160]

	strDec := string(byteSlice)          //默认按照 utf-8 解码
	fmt.Printf("utf-8 解码: %v\n", strDec) //输出解码后的字符串

	tempHex := make([]byte, 9*2)
	hex.Encode(tempHex, byteSlice)                   //按照 16 进制对字节编码
	fmt.Printf("hex 编码: %v\n", tempHex)              // 输出编码后的字节 [101 54 56 56 57 49 101 55 56 56 98 49 101 52 98 100 97 48]
	tempStr := string(tempHex)                       //将编码后的字节按照 utf-8 解码
	fmt.Printf("utf-8 对 hex 编码后的字节解码: %v\n", tempStr) //输出解码后的字符串

	fmt.Println(hex.EncodeToString(byteSlice)) // e68891e788b1e4bda0
}

输出:

1
2
3
4
5
utf-8 编码: [230 136 145 231 136 177 228 189 160]
utf-8 解码: 我爱你
hex 编码: [101 54 56 56 57 49 101 55 56 56 98 49 101 52 98 100 97 48]
utf-8 对 hex 编码后的字节解码: e68891e788b1e4bda0
e68891e788b1e4bda0

对以上输出进行分析:

首先,下面是字符串“我爱你”的 utf-8 编码

我 :230 136 145

爱 :231 136 177

你 :228 189 160

下面是字符串“e68891e788b1e4bda0”的 utf-8 编码

e : 101

6 : 54

8 : 56

string() 函数是将 []byte 按照 utf-8 解码为字符串,所以 string([]byte(name)) 会输出 “我爱你”

hex.Encode() 方法其实是将字节序列对应的十六进制字符串进行 utf-8 编码,所以 string(hex.Encode()),会输出字节对应的十六进制字符串,相当于 hex.EncodeToString()这个方法。

如何将一组任意的 []byte 转换为 hex 格式的字符串?

首先,直接使用 string() 方法是不行的,因为它会将 []byte 按照utf-8 的方式解码,而 []byte 并不是 utf-8 格式的所以会出现乱码。

反过来推,要拿到 hex 格式的字符串,我们需要找到字符串对应的 utf-8 字节数组,然后再用 string() 就可以了。现在的问题变成了,如何将字节数组转换为其 hex 字符串所对应的 utf-8 编码的字节数组

hex.Encode() 方法正好就是这个功能,将输入的字节数组转换为 hex 格式所对应的 utf-8 编码的字节数组,它的内部实现比较巧妙,可以研究一下。

hex.EncodeToString() 其实就是 string(hex.Encode()),返回hex 格式的字符串