Unicode和UTF8的关系

开发中必然会涉及到字符编码的问题,而现在UTF8是很常用的字符编码,那么它到底为何物呢,今天我就来说说他。

ASCII

相信大家对ASCII码肯定不会陌生,这里我就做一个简化版的解释,不严谨哈。
ASCII码可以理解为英文世界的字符编码表,一个字节的长度就可以囊括所有的字母数字和其他符号。
比如字母A对应为二进制的01000001。

Unicode

那么其他国家咋办呢,我们不熟悉的法文,德文,还有我们最熟悉的中文。计算机世界就产生了其他的编码来表示它们,并导致了我们头疼的乱码问题。这时,就需要一个统一的编码来让大家相互认识,于是便有了Unicode编码规范(规范是我自己理解的方式,英文原文是code point),可以理解为Unicode就是世界上所有字符的定义规范(计算机世界里的全球字典)。

Unicode只是规定了每个符号对应的二进制数值,而没有规定在计算机的物理上如何存储(ASCII码就是一个字节可以存储所有值)。

UTF-8

而UTF-8就是Unicode规范的一种实现方式(还有例如UTF-16,UTF-32)。它可以根据字符编码的值,通过使用1~4个字节的长度来存储。非常灵活,不浪费空间。

UTF-8的规定如下:

  1. 单字节符号,字节的第一位设为0,后面7位为这个符号的Unicode编码。对于英文字母,Unicode编码和ASCII码是相同的。
  2. 对于n字节符号,第一个字节的前n位都设为1,第n+1位设为0,后面所有字节的前两位统一设为10。其他的没有提到的二进制位就是用来存放Unicode编码的。
Unicode符号范围 UTF-8编码方式
0000 0000 - 0000 007F 0xxx xxxx
0000 0080 - 0000 07FF 110x xxxx 10xx xxxx
0000 0800 - 0000 FFFF 1110 xxxx 10xx xxxx 10xx xxxx
0001 0000 - 0010 FFFF 1111 0xxx 10xx xxxx 10xx xxxx 10xx xxxx

举例说明

字的Unicode编码为4E2D,在表格中的第三行范围内,我们把16进制的Unicode转化为二进制的Unicode得到:0100 1110 0010 1101。然后把这个二进制串从最后一位开始,按照第三行格式的最后一位依次往前填入, 得到:

1
2
3
1110 xxxx 10xx xxxx 10xx xxxx // UTF-8的存储格式
0100 11 1000 10 1101 // 中字的Unicode值
1110 0100 1011 1000 1010 1101 // 填空所得结果,即UTF-8的值

所以字的UTF-8编码为E4B8AD

代码实现

下面我们通过Golang来验证上面的计算。

1
2
3
4
5
6
7
8
9
10
func TestZhong(t *testing.T) {
zhong := "中"
unicodeOfZhong := []rune(zhong)
t.Log("汉字:", zhong)
t.Logf("中的Unicode:%x", unicodeOfZhong[0])

utf8OfZhong := "\xE4\xB8\xAD"
t.Logf("UTF-8输出编码值: %x", utf8OfZhong)
t.Logf("UTF-8输出汉字: %s", utf8OfZhong)
}

输出结果
1
2
3
4
汉字: 中
中的Unicode:4e2d
UTF-8输出编码值: e4b8ad
UTF-8输出汉字: 中

可以看到代码输出结果与我们手动计算的值一致,这回应该再也不会傻傻分不清楚Unicode和UTF-8的关系了吧。

加载评论框需要科学上网