⽂字编码总结
⽂字编码总结
〇、引(“联通事件”)
做⼀个试验。
新建⼀个⽂本⽂件,然后⽤记事本打开,输⼊“联通”,保存,关闭。
再次⽤记事本打开这个⽂本⽂件,你看到了什么?
这被⼈戏称是联通⼲不过移动的根本原因——连⾃⼰的名字都存不下来。
下⾯对字符的编码进⾏⼀下总结,会在其中说明联通消失的原因。
⼀、字符编码的发展
1-1、第⼀阶段:ASCII码
ASCII = American Standard Code for Information Interchange, “美国信息交换标准码”
ASCII码规定每个字符例使⽤1个字节来表⽰,也就是8位的⼆进制组合,那么就有00000000-11111111⼀共256种组合,也就是可以表⽰256个不同的字符。
但是实际上ASCII共计有128个,从0到127,也就是从00000000-01111111,最⾼位都是0。
0-31:是控制字符或通讯专⽤字符(不可以显⽰的字符,其余为可显⽰字符),如控制符:LF(换⾏)、CR(回车)等。
32-126:是字符,其中32是空格,48-57为0-9的阿拉伯数字,65-90为26个⼤写英⽂字母,97-122为26个⼩写英⽂字母,其余的是⼀些标点符号,运算符号等。
1-2、第⼆阶段:ANSI编码(本地化编码)
⽬的:解决汉字等英⽂字母以外字符的显⽰问题。
基本⽅法:使⽤最⾼位是1的字符(防⽌与ASCII冲突),2个字节表⽰⼀个汉字。
编码转换⽅法举例:
1. 以“中”字为例。采⽤的编码标准为GB2312(中国最新通⾏的⼀套兼容编码标准为GBK,⼤字符集)
2. “中”的区位码为5448(所谓区位码,就是将所有汉字都⽤4位数字来表⽰,主要应⽤于发电报,以及DOS时代的中⽂输⼊)
3. “中”的国标码为为5448的⼗六进制+2020H=5650H
4. “中”的机内码(就是我们最终要的这个编码)=国标码+8080H=D6D0H,也就是说在系统内存储为[0xD6,0xD0] 这两个字节
这些使⽤ 2 个字节来代表⼀个字符的各种⽂字延伸编码⽅式,称为 ANSI 编码。
注意ANSI编码指是“本地化”编码,在各个国家对应的编码体系是不同的。
在中⽂环境下以ANSI编码存储的⽂件,在⽇⽂环境下打开是乱码。因为⼀个是GB2312编码,⼀个是JIS编码。
(顺便吐槽,有个国标组织是很幸福的事情,⽇本通⽤的编码⽅式⾄少有四套,特么的两个公司做的系统之间通信,弄的跟国际化似的还要转换编码,⽐如:神奇的みずほ銀⾏)
1-3、第三阶段:unicode(国际化编码)
为了使国际间信息交流更加⽅便,国际组织制定了unicode字符集。
为各种语⾔中的每⼀个字符设定了统⼀并且唯⼀的数字编号,以满⾜跨语⾔、跨平台进⾏⽂本转换、处理的要求。
unicode⽤数字0-0x10FFFF来映射这些字符,最多可以容纳1114112个字符。
unicode编码中,不管什么⽂字统⼀使⽤4字节表⽰⼀个⽂字。
⼆、unicode 与 utf
2-1、utf的产⽣
unicode中,每个字符⽤ 4 个字节在存储、传输时会产⽣浪费。
UTF-8、UTF-16、UTF-32都是unicode的“紧凑”编码。都是 可变长度 编码。(所以想⼀下java中计算字符串长度时,碰到汉字的时候得到的到底是什么的长度?)
UTF = Unicode Transformation Format。
其中UTF-32使⽤32位整数编码,还是占4个字节(32bit),所以基本上不会使⽤。
UTF-8或UTF-16,分别由 8-bit 或 16-bit 为⼀个单元组成,下标值较⼩的编码点占⽤的字节数也少。
2-2、utf-8 的编码⽅式说明
utf-8 使⽤ 1~4 个不等的字节来存储字符编码。
以“郑”字为例,说明从unicode到utf-8的转换。
1. “郑”字的unicode码是 \u90d1 (可以通过这样的⽹站查询: 。或者查询unicode官⽹:)
2. 从上表中可以看到,90d1位于第三⾏(0000 0800 - 0000 FFFF),所以是使⽤三个字节存储的。其格式为 1110yyyy
10yyyyxx 10xxxxxx
3. 从“郑”的最后⼀个⼆进制位开始,依次从后向前填⼊格式中的x,多出的位补0。
4. 90d1的⼆进制码是 1001 0000 11 010001(已经按照上述格式分割),套⽤进去就是11101001 10000011 10010001,
转为⼗六进制是 0xE98391,也就是“郑”字的utf-8编码
2-3、utf-8的便利性
UTF-8 有⼀个⽅便的属性,即最开始128 个字符(ASCII字符)被编码为单个字节。
任何已经是 ASCII 编码的字符串和⽂件⽆需转换就可以被 UTF-8 识别。
⼤量的⼴泛使⽤的编程惯例——⽐如 NULL 结尾,各种控制符\n\t等——在 UTF-8 中也是可以被识别的
2-4、所谓的utf-bom、utf-⽆bom
使⽤ NotePad++ 这样的⽂本编辑器时,可以将⽂件“以 UTF-8 ⽆ BOM 格式编码”。
所谓的BOM,全称是Byte order mark。
作⽤是在⽂件最开始加⼊⼀个标识符,让⽂本编辑器明确知道读⼊的⽂件是以何种⽅式编码的。
常⽤的BOM如下:
UTF-8:EF BB BF
UTF-16:FE FF
unicode:FF FE (这说法其实不是特别准确,参看后⾯的2-8节)
2-5、联通为什么保不住⾃⼰的名字?
记事本默认的编码是 ANSI,对于中⽂系统中就是 GBK 编码。
“联”字的编码是 0xc1aa,⼆进制 1100 0001 1010 1010。
“通”字的编码是 0xcda8,⼆进制 1100 1101 1010 1000
→ 这玩意跟编码规则中第⼆⾏是相符的。
所以记事本再次打开这个⽂件的时候,将其识别成了“UTF-8 ⽆ BOM 格式”,所以全程按照utf-8编码规则解析,就变成了乱码。
⼈家移动俩字就没这事。电信啥的也都没事。
结论:当⽂档中的所有字符的⼆进制编码在C0≤AA(第⼀个字节)≤DF 80≤BB(第⼆个字节)≤BF时,记事本都⽆法确认⽂本的编码格式,就按照UTF-8的格式来显⽰。
2-6、UCS 编码
在第⼀章提到的第三个阶段(国际化)的初期,其实有两套国际化编码。
1. 国际标准化组织(ISO),1984年制定⼀份“通⽤字符集”(Universal Character Set,简称UCS),并最终制定了ISO 10646
标准
2. 统⼀码联盟,由Xerox、Apple等软件制造商于1988年组成,并且开发了Unicode标准
3. 1991年,两个组织开始合并双⽅的⼯作成果,创⽴了单⼀编码表。但是两套标准仍然独⽴存在,只是unicode使⽤传播更为⼴泛。UCS-2 是 ISO 10646标准为“通⽤字符集”(UCS)定义的16位固定长度编码。
它包含65536个编码空间,存储的是全世界最常⽤的65536个字符编码。
可以认为 UCS-2 是 UTF-16 的⼀个⼦集,编码相同。其实UCS-2就是原始的双字节Unicode编码。
2-7、Little endian 和 Big endian
UCS-2 这种两字节定长编码,在存储的时候,有两种格式。
参看 Notepad++ 的编码菜单,⾥⾯有“以 UCS-2 Little endian 格式编码”以及“以 UCS-2 Big endian 格式编码”
⽐如“郑”的编码是 90D1 (没错,对于这个字的编码,unicode、ucs-2和utf-16是相同的)
如果存储为 90D1,叫做BE(Big endian);倒过来存为 D190 的话,称为LE(Little endian)。
习惯windows系统的⼈可能根本没见过LE,但是在Unix/Linux中这种情况并不少见。
在 UCS-2/unicode(兼容) 编码标准中,规定在每⼀个⽂件的最前⾯分别加⼊⼀个表⽰编码顺序的字符,这个字符的名字叫做"零宽度⾮换⾏空格"(zero width no-break space)。
如果头两个字节是 FE FF,就表⽰该⽂件采⽤⼤端⽅式
如果头两个字节是 FF FE,就表⽰该⽂件采⽤⼩端⽅式
→ 等等,⽂件头上的信息不是BOM吗?
2-8、BOM的更完整、更准确的表述
完整的BOM编码
UTF-8:EF BB BF
UTF-16 (big-endian):FE FF
UTF-16/UCS-2 (little-endian):FF FE
UTF-32/UCS-2 (big-endian):00 00 FE FF
UTF-32 (little-endian): FF FE 00 00
也就是说,表达编码种类以及BE、LE的⼯作都是由BOM来完成的
2-8-1、关于 UTF-8 的BOM
其实Linux默认UTF-8编码应该不带BOM的。
尽管 Unicode 标准允许在 UTF-8 中使⽤ BOM,但不含 BOM 的 UTF-8 才是标准形式,在 UTF-8 ⽂件中放置 BOM 主要是微软的习惯。
2-8-2、为什么2-5的列表中,把BOM为FF FE的编码标记为unicode?
因为把带有BOM的⼩端(LE)的 UTF-16 称作「Unicode」也是微软的习惯
2-8-3、对于⽆BOM的UTF-8⽂档,windows的⽂本编辑器到底是怎么判断的?
猜(参看联通事件)
2-9、MySQL中的utf-8
mysql⽀持的 utf8 编码最⼤字符长度为3字节,⽽标准的utf-8最⼤字符长度为4字节。
三个字节的 UTF-8 最⼤能编码的 Unicode 字符是 0xffff,也就是 Unicode 中所谓的“基本多⽂种平⾯(BMP)”。能够应对绝⼤多数应⽤场景。
(MySQL刚开发的时候,unicode本⾝也没有提出“辅助平⾯”,所以3字节的设计是⽆可厚⾮的)
但是包括 Emoji 表情、⼀些特殊汉字在内的字符是⽆法存储的。
MySQL 5.5.3 版本以后,推出utf8mb4字符集,⽤来对应标准的utf-8。
2-10、unicode的编码规则
可以参看这篇⽂章中的“Unicode 介绍”⼀节
三、Base64
3-1、编码⽬的
简单来说,就是把所有字符统⼀转换成可见字符。
Base64是⼀种基于64个可打印字符来表⽰⼆进制数据的表⽰⽅法。(ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/)
Base64常⽤于在通常处理⽂本数据的场合,表⽰、传输、存储⼀些⼆进制数据,包括MIME的电⼦邮件及XML的⼀些复杂数据。
3-2、编码规则
由于 2的6次幂=64,所以Base64编码中,以6个⽐特为⼀个单元,对应某个可打印字符。
⽐如,3个字节⼀共24⽐特,那么就对应4个Base64单元。
也就是说,编码后的数据长度为原来的 4/3。
若原数据长度不是3的倍数时且剩下1个输⼊数据,则在编码结果后加2个=;若剩下2个输⼊数据,则在编码结果后加1个=。
url编码和utf8区别举例:
如果要编码的字节数不能被3整除,最后会多出1个或2个字节,那么可以使⽤下⾯的⽅法进⾏处理:先
使⽤0字节值在末尾补⾜,使其能够被3整除,然后再进⾏Base64的编码。在编码后的Base64⽂本后加上⼀个或两个=号,代表补⾜的字节数。也就是说,当最后剩余两个⼋位字节(2个byte)时,最后⼀个6位的Base64字节块有四位是0值,最后附加上两个等号;如果最后剩余⼀个⼋位字节(1个byte)时,最后⼀个6位的base字节块有两位是0值,最后附加⼀个等号。
3-3、UTF-7
UTF-7是⼀个修改版Base64(Modified Base64)。
主要是将UTF-16的数据,⽤Base64的⽅法编码为可打印的ASCII字符序列。⽬的是传输Unicode数据。
主要的区别在于不⽤等号=补余,因为该字符通常需要⼤量的转译。
四、urlcode
4-1、编码⽬的
URL编码(URL encoding),也称作百分号编码(Percent-encoding)。
适⽤于统⼀资源标识符(URI)的编码,也⽤于为"application/x-www-form-urlencoded" MIME准备数据。
4-2、编码规则
将需要转码的字符转为16进制,然后从右到左,取4位(不⾜4位直接处理),每2位做⼀位,前⾯加上%,编码成%XY格式。
举例1:
空格ASCII码是32,对应16进制是20,那么urlencode编码结果是:%20,但在最新标准(RFC-1738)中空格对应的是+。
举例2:
“中”的GB2312码是0xD6D0,那么urlencode编码结果是:%D6%D0

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。