Go 语言
标准库、反射和 cgo
字符集原理

字符集原理

Go 语言源码使用 Unicode 字符集,并采用 UTF-8 编码方案。Go 还提供了 rune 原生类型来表示 Unicode 字符。

字符和字符集

由计算机编码表示的代表人类自然语言编写程序时,程序员直接操作的不再是二进制数据,而是由计算机编码表示的代表人类自然语言符号的字符。 当向计算机输入指令或数据时,翻译程序会将字符翻译为字符并显示在终端上。

计算机中的数据存储和传输都使用的是比特(bit),因此字符也是用比特来表示。 究竟用多少比特来表示一个字符,就变成确定编码空间的问题,要确定编码空间就要知道计算机中要使用的字符总数是多少,而所有这些字符组成的集合就是字符集。

ASCII 码是美国信息交换标准代码(American Standard Code for Information Interchange)的缩写,是基于拉丁字母的一套电脑编码系统,主要用于显示现代英语和其他西欧语言。

计算机字符集中的每个字符都有两个属性:码点和表示这个码点的内存编码。

Unicode 字符集

1994年,人类终于有了收纳全球所有字符为目的的统一字符集。Unicode 叫 Universal Multiple-Octet Coded Character Set 的缩写。其中文含义是“通用多字节编码字符集”。

Unicode 字符集致力于为全世界现存的每种语言中的每个字符分配一个统一且唯一的字符编号,以满足跨语言、跨平台进行文本数据转换、处理、存储和显示的需求。

UTF-8 使用变长度字节对 Unicode 进行编码。编码采用的字节数量与 Unicode 字符在码点表中序号有关。UTF-8 编码规则如下:

  • 单字节字符的第一位设为 0,后面 7 位为 Unicode 字符码点。
  • 多字节字符的第一个字节的前 n 位设为 n 个 1,n+1 位设为 0,后面字节的前两位都设为 10。剩下的位数用于存放 Unicode 字符码点。

UTF-8 方案与 ASCII 码兼容,也就是说,UTF-8 编码的字符的第一个字节的值与 ASCII 码兼容。

UTF-8 编码方案由于优点众多,已经成为 Unicode 字符在计算机中内存编码表示方案的事实标准。Go 语言顺应了这一趋势,其源码文件的字符编码采用的也是 UTF-8 编码方案。

rune 类型

在 Go 中,每个 rune 对应一个 Unicode 字符的码点,而 Unicode 字符在内存中的编码表示则放在 []byte 类型中。从 rune 类型转换位 []byte 类型称为编码,反过来就是解码。

// rune -> []byte
func encodeRune() {
    var r rune = 0x4E2D // 中
    buf := make([]byte, 3)
    n := utf8.EncodeRune(buf, r)
 
    fmt.Println(n, buf) // 3 [228 184 173]
    fmt.Println(string(buf)) // 中
}
 
// []byte -> rune
func decodeRune() {
    buf := []byte{228, 184, 173}
    r, size := utf8.DecodeRune(buf)
 
    fmt.Println(r, size) // 19990 3
}