熟悉计算机的同学一定听说过编码字符集这么个名词。utf-8作为当下被广泛应用的编码字符集,大家也一定有所耳闻。
对于电子计算机而言,处理器只能识别二进制机器码。所以任何人类可以进行阅读的电脑文件,其实只是一系列0和1的组合。是字符编码规则,将机器可以识别的二进制内容转换为人类可以识别的各种文档。
最早的编码字符集ACSII,使用1字节表示128个字符,一个字节是8位,也就是从0~127(二进制区间为00000000~01111111),每一个数值都对应着一个字符。但是ASCII是为英语设计的,不能表示诸如汉语等其他语言的字符。
针对这个问题,大家可能会觉得,我们只要使用更多的字节,就可以有更大的整数范围去和世界上所有的字符一一对应。这当然是一种解决问题的办法,然而单纯的扩大字节数是有弊端的。假设我们可以使用4个字节表示所有字符,文件中每个字符都使用4个字节进行表示,结果原本ASCII中仅需要一个字节就能表示的字符也占用了4个字节的空间,这就造成了极大的浪费,要知道早期计算机的存储设备不仅价格高昂而且容量小。于是变长编码字符集应运而生,我们今天常见的utf-8就是变长编码字符集的一种。
utf-8字符集与ACSII字符集是兼容的,也就是说可以被ASCII正常解读的文件,可以被utf-8正常解读,而且结果一致。那么utf-8究竟是怎么进行编码的呢?
我们发现ACSII虽然使用了一个字节来表示字符,但是最高位是没有使用的。utf-8也使用每个字节的头几位来记录关于字符编码的信息。
例如对于汉字而言,utf-8需要3个字节来进行表示,一般长这个样子:1110xxxx 10xxxxxx 10xxxxxx。对于第一个字节,使用二进制111表示这个字符需要3个字节,再加上一个0与实际要记录的值隔开,最后4位是该字符实际二进制值的最高4位。对于第二第三个字节,最高两位使用10占位,剩下的6位分别是该字符实际二进制值的中间6位和最后6位。
与上面的表达方式类似的,2个字节表示的字符,在二进制下表示为:110xxxxx 10xxxxxx;4个字节表示的字符,在二进制下表示为:11110xxx 10xxxxxx 10xxxxxx 10xxxxxx;5个字节表示的字符,在二进制下表示为:111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx;6个字节表示的字符,在二进制下表示为:1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx。
实际上对于我们平时使用而言,4个字节已经足够了(即除ACSII字符外,还可以表示2^21-1个字符,约200万个字符)。而且使用超过4个字节表示字符也是不合utf-8规范的。
当计算机根据utf-8编码解码文件时,如果遇到一个字节是以10开头的,那么立刻可以知道这不是一个字符的开头。通过向前或向后查找以110、1110、11110开头的字节开始进行解码。而当遇到以0开头的字节时,自然按照ACSII码来进行处理。
下面我使用go语言编写一段代码,示例utf-8的编码方式:
```go
s :=`中a`
fmt.Println("字节长度",len(s))
for i :=0;i
fmt.Printf("十进制字节值: %d,二进制字节值: %08b\n",s[i],s[i])
}
fmt.Println("=======")
for i,r :=range s {
fmt.Printf("字符在串中的位置: %d,字符: %c,二进制码: %08b\n",i,r,r)
}
```
输出结果为:
字节长度 4
十进制字节值: 228,二进制字节值: 11100100
十进制字节值: 184,二进制字节值: 10111000
十进制字节值: 173,二进制字节值: 10101101
十进制字节值: 97,二进制字节值: 01100001
=======
字符在串中的位置: 0,字符: 中,二进制码: 100111000101101
字符在串中的位置: 3,字符: a,二进制码: 01100001
结果印证了上文中的描述。
网友评论