背景知识
Bit:
比特(Bit),也称二进制位,指二进制中的一位,是计算机信息的最小单位。Bit是Binary digit(二进制数位)的缩写,还可被缩写为b。
字节(港澳台称位元组,Byte),一个字节代表8个比特,也被缩写为B,在工业标准、网络、电信技术中也被成为八位组(Octet)。
字面量,可以理解为给人看的内容,比如在python代码中有:
str_demo
str_demo = "abc"
str_demo_1 = "中国"
abc 和 中国 就是字面量,而字面量的值即默认工作字符集的编码。
编码的演进故事
编码:
编码即转换,字符编码(character encoding),是把字符集中的字符编码为指定集合中的某一对象(比特、自然数序列、电脉冲),以便文本在计算机中存储和通过网络传递。比如:将拉丁字母表编码成ASCII。而ASCII将字母、数字和其它符号编号,并用7比特的二进制来表示这个整数,通常会额外使用一个扩充的比特,比便于以一个字节的方式存储。
内码 & ASCII & ANSI:
字符内码(character code)指的是用来代表(编码)字符(字面量)的计算机内码,程序在输入和存储文档时都要使用内码,内码分为:单字节内码,可以支持256个字符编码,双子节内码可以支持65000个字符编码,前者为ASCII,后者对应ANSI。
ASCII(发音:/ˈæski/)
American Standard Code for Information Interchange,美国信息交换标准代码,是基于拉丁字母的一套编码系统。它主要用于现实现代英语,至今为止(1986年)共定义了128个字符;其中95个可显示字符(空格也算)其它33个字符是图案或多已废弃的控制符。
而我们博大精深的中文(还有很多其它亚洲文字),文字N多,所以使用了双字节的方式,GB2312(简体中文编码的一种),实际上是ANSI的一个代码页(936),使用不同代码页的内码无法在其它代码页正常显示,中英混排字数统计等问题仅仅使用扩展ASCII是比较痛苦的。
那么能不能用一种编码集应对所用文字呢?于是乎伟大的Unicode诞生了
Unicode:
也被称为国际码,统一码,它对大部分文字进行了整理编码,它伴随着通用字符集(Universal Character Set, UCS)的标准而发展,到2016年6月21日公布的9.0.0版本,已经收入了10万+个字符。
然而这个世界不是理想的,不可能一夜之间所有系统都使用Unicode来处理字符,所以在Unicode诞生之日就面临一个严峻的考验:兼容!
UTF-8:
所有字符的Unicode编码是确定的,但是在传输过程中,由于不同系统平台的设计不一定一致,以及出于节省空间的目的,对Unicode编码的实现方式有所不同。Unicode的实现方式称为Unicode转换格式(Unicode Transformation Format)
utf-8可以说是让Unicode能够真正落地实施的方案,它以8位为单元对Unicode进行变长编码。它将基本7位ASCII字符仍用7位编码表示,占用一个字节(首位补0)。而遇到与其他Unicode字符混合的情况,将按一定算法转换,每个字符使用1-3个字节编码,并利用首位为0或1进行识别。
从Unicode到UTF-8的编码转换方式如下:
Unicode编码(16进制) UTF-8 字节流(二进制)
0000 - 007F 0xxxxxxx
0080 - 07FF 110xxxxx 10xxxxxx
0800 - FFFF 1110xxxx 10xxxxxx 10xxxxxx
此外Unicode的实现方式还有UTF-7、UTF-16、UTF-32、Punycode、CESU-8、SCSU、GB18030等,这些实现方式有些仅在一定的国家和地区使用,有些则属于未来的规划方式。
目前通用的实现方式是UTF-16小端序(LE, Little Endian)、UTF-16大端序(BE, Big Endian)和UTF-8。
字节序和BOM
双字节编码一个字符的时候,就会遇到字节序的问题,比如:“中”的Unicode编码是4e 2d,大数在前就是大端。
BOM(Byte Order Mark)
主要目的是表明编码字节序,实现上是一个有点小聪明的想法:
在UCS编码中有一个叫做”ZERO WIDTH NO-BREAK SPACE”的字符,它的编码是FEFF。而FFFE在UCS中是不存在的字符,所以不应该出现在实际传输中。UCS规范建议我们在传输字节流前,先传输字符”ZERO WIDTH NO-BREAK SPACE”。
这样如果接收者收到FEFF,就表明这个字节流是Big-Endian的;如果收到FFFE,就表明这个字节流是Little-Endian的。因此字符”ZERO WIDTH NO-BREAK SPACE”又被称作BOM。
UTF-8不需要BOM来表明字节顺序,但可以用BOM来表明编码方式。字符”ZERO WIDTH NO-BREAK SPACE”的UTF-8编码是EF BB
Python编码问题Tip
1、Unicode做为中间码传输,显示的时候再根据需要转码
2、Python中有个包叫chardet,它是用来猜测编码的,注意是猜测
3、如果一个字符串的字面量保存成了编码,想要变回真正的字面量时,通用的做法需要用正则搞一下,然而往往情况比较简单,只需要decode(string_escape),比如下面这个字符串的内容:
>>> abc = "\\xe4\\xb8\\xadabc\\xe5\\x9b\\xbd123"
>>> print abc
\xe4\xb8\xadabc\xe5\x9b\xbd123
>>> print abc.decode('string_escape')
中abc国123