众所周知,计算机里的所有信息都表示为一个二进制字符串,而编码的作用就是建立一个人类字符和二进制位之间的映射,来方便人与计算机之间、人与人之间的信息交流。
ASCII码:
American Standard Code for Information Interchange,美国信息互换标准代码。
上个世纪60年代,美国制定了一套字符编码,对英语字符与二进制位之间的关系,做了统一规定。这被称为ASCII码,一直沿用至今。
编码规则:
ASCII码一共规定了128个字符的编码,比如空格“SPACE”是32(二进制00100000),大写的字母A是65(二进制01000001)。这128个符号(包括32个不能打印出来的控制符号),只占用了一个字节的后面7位,最前面的1位统一规定为0。
ASCII码用了1个字节,1个字节可以表示256种状态,但ASCII码只用了128种,也就是一个字节的后七位,最前面的1位都是0。
各个国家自己的编码:
由于ASCII码只有规定了128个字符,没有考虑到其他国家的字符,比如中文字符,所以不同国家继承ASCII码建立了自己的编码。
比如中文字符编码:
我们不客气地把那些127号之后的奇异符号们直接取消掉, 规定:一个小于127的字符的意义与原来相同,但两个大于127的字符连在一起时,就表示一个汉字,前面的一个字节(称之为高字节)从0xA1用到0xF7,后面一个字节(低字节)从0xA1到0xFE,这样我们就可以组合出大约7000多个简体汉字了。在这些编码里,我们还把数学符号、罗马希腊的字母、日文的假名们都编进去了,连在 ASCII 里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的”全角”字符,而原来在127号以下的那些就叫”半角”字符了。中国人民看到这样很不错,于是就把这种汉字方案叫做 “GB2312“。GB2312 是对 ASCII 的中文扩展。
后来发现不够用:
于是我们不得不继续把GB2312 没有用到的码位找出来老实不客气地用上。后来还是不够用,于是干脆不再要求低字节一定是127号之后的内码,只要第一个字节是大于127就固定表示这是一个汉字的开始,不管后面跟的是不是扩展字符集里的内容。结果扩展之后的编码方案被称为 GBK 标准,GBK包括了GB2312 的所有内容,同时又增加了近20000个新的汉字(包括繁体字)和符号。 后来少数民族也要用电脑了,于是我们再扩展,又加了几千个新的少数民族的字,GBK扩成了 GB18030。
这种中文字符编码方式被统称为“DBCS“(Double Byte Charecter Set 双字节字符集),也是为什么我们常说的,中文字符编码长度是英文的两倍。
Unicode字符集:
每个国家都有自己的一套编码,非常不利于国际交流,所以ISO(国际标准化组织)推出了一个包含地球上所有字符的编码集——”Universal Multiple-Octet Coded Character Set”,简称 UCS, 俗称 “Unicode“。
Unicode编码集虽然规定了字符该用哪个二进制串表示,但是有的字符只需要一个字节,而有的字符却需要多达四个字节,如果都用4个字节来表示一个字符的话,那么每个英文字母前都必然有二到三个字节是0,这对于存储来说是极大的浪费,文本文件的大小会因此大出二三倍,这是无法接受的。
Unicode编码规则:
Unicode编码指的是UCS-2编码方式,即直接用两个字节存入字符的Unicode码。(这种情况下如果一个字符的unicode超过2个字节就存不下了)
UTF-8:
互联网的普及,强烈要求出现一种统一的编码方式。UTF-8就是在互联网上使用最广的一种Unicode的实现方式。UTF(UCS Transfer Format)即Unicode字符集的编码规则。
UTF-8 与 Unicode区别:
- Unicode 是 「字符集」
- UTF-8 是 「编码规则」
字符集:为每一个字符分配一个唯一的ID(码位/码点/Code Point) ;
编码规则:将码位转换为字节序列的规则(编码/解码 => 加密/解密)。
UTF-8的编码规则:
UTF-8是一种变长字节编码方式。最小编码单位(code unit)为一个字节。一个字节的前1-3个bit为描述性部分,后面为实际序号部分。
- 对于单字节字符,占用一个字节空间。0之后的所有部分(7个bit)代表Unicode中的序号。因此对于英文字母,UTF-8编码和ASCII码是相同的。
- 对于n字节字符,第一个字节的前n为都为1,第n+1为0,后面字节的前两位上为10。剩下没有提及的二进制位为这个符号的unicode码。
Unicode符号范围「十六进制」 | UTF-8编码「二进制」 |
---|---|
0000 0000-0000 007F | 0xxxxxxxx |
0000 0080-0000 07FF | 110xxxxx 10xxxxxx |
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx |
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx |
综上: 如果一个字节的第一位是0,则这个字节单独就是一个字符;如果第一位是1,则连续有多少个1,就表示当前占用多少个字节。
“严”的unicode是4E25(100111000100101),根据上表,可以发现4E25处在第三行的范围内(0000 0800-0000 FFFF),因此“严”的UTF-8编码需要三个字节,即格式是“1110xxxx 10xxxxxx 10xxxxxx”。然后,从“严”的最后一个二进制位开始,依次从后向前填入格式中的x,多出的位补0。这样就得到了,“严”的UTF-8编码是“11100100 10111000 10100101”,转换成十六进制就是E4B8A5。
Little endian和Big endian:
之前提到了Unicode一般使用UCS-2编码方式,有两种格式:Little endian和Big endian。
以汉字”严“为例,Unicode码是4E25,需要用两个字节存储,一个字节是4E,另一个字节是25。存储的时候:
- 4E在前,25在后,就是Big endian方式;
- 25在前,4E在后,就是Little endian方式。
计算机怎么知道某一个文件到底采用哪一种方式编码?
Unicode规范中定义,每一个文件的最前面分别加入一个表示编码顺序的字符,这个字符的名字叫做”零宽度非换行空格“(ZERO WIDTH NO-BREAK SPACE),用FEFF表示。这正好是两个字节,而且FF比FE大1。
如果一个文本文件的头两个字节是FE FF,就表示该文件采用大头方式;如果头两个字节是FF FE,就表示该文件采用小头方式。
Base64:
Base64是一种基于64个可打印的字符来标识二进制数据的标识方法。可打印的字符包括字母A-Z、a-z、数字0-9,这样共有62个字符,此外两个可打印的符号在不同系统中而不同。
在MIME格式的的电子邮件中,另外两个符号为加号+和斜杠/,等号=用来做后缀用途。
编码转换方式
- 将每三个字节作为一组,一共24个二进制位。
- 将这24个二进制位分为4组,每个组有6个二进制位。
- 在每组前面加两个00,扩展成32个二进制位,及四个字节。
- 然后根据下表得到扩展后的每个字节的编号,这就是Base64的编码值。
索引 | 对应字符 | 索引 | 对应字符 | 索引 | 对应字符 | 索引 | 对应字符 |
---|---|---|---|---|---|---|---|
0 | A | 17 | R | 34 | i | 51 | z |
1 | B | 18 | S | 35 | j | 52 | 0 |
2 | C | 19 | T | 36 | k | 53 | 1 |
3 | D | 20 | U | 37 | l | 54 | 2 |
4 | E | 21 | V | 38 | m | 55 | 3 |
5 | F | 22 | W | 39 | n | 56 | 4 |
6 | G | 23 | X | 40 | o | 57 | 5 |
7 | H | 24 | Y | 41 | p | 58 | 6 |
8 | I | 25 | Z | 42 | q | 59 | 7 |
9 | J | 26 | a | 43 | r | 60 | 8 |
10 | K | 27 | b | 44 | s | 61 | 9 |
11 | L | 28 | c | 45 | t | 62 | + |
12 | M | 29 | d | 46 | u | 63 | / |
13 | N | 30 | e | 47 | v | ||
14 | O | 31 | f | 48 | w | ||
15 | P | 32 | g | 49 | x | ||
16 | Q | 33 | h | 50 | y |
Base64字符标中的字符原本用6bit就可以表示,现在前面添加2个0,变为8bit,导致Base64编码后的文本大小比原文大约三分之一。
位数不足3位处理
-
二位的情况:两个字节共16个二进制位,按照上面方式分组,每6个一组,则第三组缺少2位,用0补齐。如”Ma”可以转换为三组00010011、00010110、00010000,对应的Base64值分别为T、W、E,再补上一个”=”号,因此”Ma”的Base64编码为”TWE=”;
-
一位的情况:一字节共8个二进制位,按照上面方式分组,每6个一组,则第二组缺少4位,用0补齐。如”M”可以转换为00010011、00010000,对应的Base64位的值分别为:T、Q,再补上两个”=”号,因此”M”的Base64编码就是”TQ==”;
注意事项
- 大多数编码都是由字符串转换为二进制的过程,而Base64的编码则是从二进制转为字符串。
- Base64主要用在传输,存储,表示二进制领域,不能算得上加密,只是无法直接看到铭文。
- 中文有很多编码(比如:UTF-8、GB2312、GBK等),不同的编码对应的Base64编码结果不一样。
最后一首诗总结一下:
手持两把锟斤拷
口中疾呼烫烫烫
脚踏千朵屯屯屯
笑看万物锘锘锘