一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第24天,点击查看活动详情。
计算机中储存的信息都是用二进制数表示的,而我们在屏幕上看到的英文、汉字等字符是二进制数转换之后的结果。通俗的说,按照何种规则将字符存储在计算机中 (如'a'用什么表示) 称为编码;反之,将存储在计算机中的二进制数解析显示出来,称为解码,如同密码学中的加密和解密。在解码过程中,如果使用了错误的解码规则,则将导致'a'解析成'b'或者乱码。
一、基础概念
1. 字节 (byte)
字节 (byte) 是计算存储容量的一种计量单位。规定1字节 (byte) =8位 (bit) 。例如 :00001111 这个8位的二进制数就占了一个字节的存储容量。
计算机是通过0和1组成的二进制位来表达万物的。其中,每1位 (bit) 中只能存储0或者1,这样多个位就形成了某个特定的含义。
2. 字符 (Character)
字符 (Character) 是各种文字和符号的总称,包括各国家文字、标点符号、图形符号、数字等。如我国有汉字符号,汉字分为简体和繁体;俄罗斯有俄语符号;日本有日语符号...这些符号都属于字符。
3. 字符集
字符集有广义和狭义之分。
- 广义的字符集是一个关于字符规则的集合,包括:字库表 (character repertoire) 、编码字符集 (coded character set) 、字符编码 (character encoding form) 。
- 狭义的字符集就是指的编码字符集,如无说明,下面所说的字符集都默认为广义字符集。
字库表,是一个存储该字符集中的所有字符 (包括控制字符、可显示字符等) 的数据库,字库表决定了整个字符集能够展现表示的所有字符的范围。
编码字符集,即用一段连续的二进制地址来对应字库表中的字符,这个二进制地址也叫做编码值 (code point) ,而这个值也称为某一个字符在编码字符集中的序号。编码字符集有很多种,每编码字符集中所包含的字符个数也各不不同,常见编码字符集有:Unicode、ASCII、GB2312、GBK、 GB18030等。
- Unicode:也叫统一编码字符集,它包含了几乎世界上所有的已经发现且需要使用的字符 (如中文、日文、英文、德文等) 。
- ASCII:早期的计算机系统只能处理英文,所以ASCII也就成为了计算机的缺省编码字符集,包含了英文所需要的所有字符。
- GB2312:中文编码字符集,包含ASCII字符集。ASCII部分用单字节表示,剩余部分用双字节表示。
- GBK:GB2312的扩展,完整包含了GB2312的所有内容。
- GB18030:GBK字符集的超集,常叫大汉字字符集,也叫CJK (Chinese,Japanese,Korea) 字符集,包含了中、日、韩三国语言中的所有字符。
字符编码,是定义在编码字符集上的一种规则,这种规则定义了编码字符集中的编码值 (code point) 和实际存储值之间的转换关系。而字符,恰好就是根据字符编码方案转换为一个二进制数值存储在计算机中。
二、编码字符集和字符编码实例
1.ASCII
ASCII既是编码字符集,又是字符编码。也就是说ASCII直接将字符在编码字符集中的序号作为字符在计算机中的存储值。如:在ASCII中A排第65位,序号是0100 0001 (65) ,编码后A的存储值也是0100 0001 (65) 。
2.Unicode
Unicode编码字符集的字符编码有UTF-8、UTF-16、UTF-32等多种字符编码,常用的是UTF-8。
UTF-8编码为变长编码。UTF-8不是编码规范,而是编码方式。UTF-8的最小编码单位 (code unit) 为一个字节。
一个字节的前1-3个bit为描述性部分,后面为实际序号部分。编码规则如下:
- 如果一个字节的第一位为0,那么代表当前字符为单字节字符,占用一个字节的空间。0之后的所有部分 (7个bit) 代表在Unicode中的序号。如:0100 0001我们就是用0100 0001来表示,对于一个字节的字符,其实就是直接使用地址表示。
- 如果一个字节以110开头,那么代表当前字符为双字节字符,占用2个字节的空间,且第二个字节以10开头。110之后的所有部分 (5个bit) 加上后一个字节的除10外的部分 (6个bit) 代表在Unicode中的序号。
- 如果一个字节以1110开头,那么代表当前字符为三字节字符,占用3个字节的空间,且第二、第三个字节以10开头。1110之后的所有部分 (4个bit) 加上后两个字节的除10外的部分 (12个bit) 代表在Unicode中的序号。
- 如果一个字节以11110开头,那么代表当前字符为四字节字符,占用4个字节的空间。且第二、第三、第四个字节以10开头。11110之后的所有部分 (3个bit) 加上后两个字节的除10外的部分 (18个bit) 代表在Unicode中的序号。
| Unicode中的字符序号 | bit数 | 编码后的UTF-8值 | byte数 |
|---|---|---|---|
| 0000 0000 ~0000 007F | 0~7 | 0XXX XXXX | 1 |
| 0000 0080 ~0000 07FF | 8~11 | 110X XXXX 10XX XXXX | 2 |
| 0000 0800 ~0000 FFFF | 12~16 | 1110 XXXX 10XX XXXX 10XX XXXX | 3 |
| 0001 0000 ~001F FFFF | 17~21 | 1111 0XXX 10XX XXXX 10XX XXXX 10XX XXXX | 4 |
根据UTF-8编码规则可知,英文在UTF-8字符编码后只占1个字节,中文占了3个字节。
Ps:Unicode转换为UTF-8需要的字节数可以根据这个规则计算:如果Unicode小于0x80 (Ascii字符) ,则转换后为1个字节。否则转换后的字节数为Unicode二进制位数+3再除以5。
Ps:Unicode转换为UTF-8时,可以将Unicode二进制从低位往高位取出二进制数字,每次取6位,并按格式填补。
3.编码转换
以'胡'字为例,胡在各种字符编码下十六进制存储值:
| 编码 | 值 |
|---|---|
| ANSI编码 | BA FA |
| Unicode big endian编码 | FE FF 80 E1 |
| Unicode编码 | FF FE E1 80 |
| UTF-8编码 | E8 83 A1 |
①ANSI字符编码是一种默认字符编码,对于英文字符默认使用ASCII编码,对于简体中文文件默认使用GB2312编码。
②Unicode big endian 和 Unicode 编码都是以2个字节为一组进行存储,但 Unicode 编码将低位字节放前面,而Unicode big endian将高位字节放后面。UTF-8是根据Unicode big endian的存储值来进行编码转换的。
Unicode big endian编码用后面两个字节存储汉字,其与UTF-8编码转换过程如下:
胡的unicode big endian编码值 (80E1) :
1000 000011 100001
胡的UTF-8编码值 (E8 83 A1) :11101000 10000011 10100001