Fork me on GitHub

Golang中的Unicode、UTF-8与字节序列

在对Go语言的字符串进行操作时,都会遇到 unicode、UTF-8和字节序列这几个词,一直对这几个名称之间的关系一知半解。今次,我们就来理清这里的关系。

Unicode定义了一个字符与一个编码的映射,但是,对应的存储却没有制定。就拿一个大写字符A来说,对应编码是0x0041,但因此存储规则没有制定,那么,如果我们用4个字节来存储,就是0x00000041。这种规定了用几个字节来存储的方式,就可以说是Unicode编码规范的具体实现。比如,UTF-8(最少用一个字节就能表示一个字符的编码实现,最多使用四个字节)和UTF-16(最少用两个字节能表示一个字符的编码实现)。

Go语言中的string类型值由若干个Unicode字符(也叫Unicode码点)组成,每个Unicode字符都可以由一个rune类型的值来承载。这些字符在底层都会转换为UTF-8编码值,而UTF-8编码值又会以字节序列的形式来表达和存储,因此,一个string类型的值在底层就是一个能够表达若干个UTF-8编码值的字节序列。

我们常使用for …range 语句对string进行遍历,例如:

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

import "fmt"

func main() {
str := "UTF 码点 "
for i, c := range str {
fmt.Printf("%d: %q [% x]\n", i, c, []byte(string(c)))
}
}

/*
索引 Unicode码点 字节序列(字符存储使用的是UTF-8编码,至少使用一个字节,最多四个字节,英文一个字节,中文三个字节)
0: 'U' [55]
1: 'T' [54]
2: 'F' [46]
3: ' ' [20]
4: '码' [e7 a0 81]
7: '点' [e7 82 b9]
10: ' ' [20]
*/

这段代码,可以看出,for语句逐一迭代的是字符串里的每个Unicode字符,字符串的底层实现是字节序列,使用UTF-8编码,根据Unicode字符的不同而选择不同的字节长度来存储这个字符。所以说,迭代的时候,就是在字节序列里,去迭代每一个UTF-8编码值,也就是每一个Unicode字符。

总结:字符串string就是一个个Unicode字符组成的,底层使用UTF-8编码格式来存储Unicode字符(UTF-8编码方案是Unicode编码规范的一种具体实现,使用至少一个字节来存储Unicode字符,最多四个字节)。