编码问题是一个很大很杂的话题,要向彻底的讲明白可以写一本书了。导致乱码的原因很多,系统平台、编程语言、多国语言、软件程序支持、用户选择等都可能导致无法正确的解析编码。
导致乱码的主要原因可以简单归结于文本的编码方式和解码方式不同导致的。本文将通过在win7(zh-cn)系统下分析python2.7的编解码问题来简单窥探一下编码的冰山一角。
今后遇到编码问题时能够多一点分析解决思路,要是能起到一个抛砖引玉的作用,那就再好不过了。
1.为什么需要编码
物理存储介质上的基本存储单元只能有两种状态,使用0和1区分。一段连续的0,1序列可以表示特定的信息。
理论上任何字符都可以被唯一的一个连续0,1组成的bit序列表示。如果这样的一个bit序列也唯一的代表一个字符,那么我们可以由此建立一个bit序列和字符之间的转换关系。
数字0 - 9和字母a - z(A - Z)等是人类可识别的字符,但是无法存入到计算机的存储介质中。
十进制 二进制(bit 序列) 字符48 0011 0000 0
65 0100 0001 A
97 0110 0001 a
为此,我们将这些人类可识别的有意义的字符与特定0,1组成的bit序列建立起一一对应的关系。
由字符转成对应的bit序列的过程称为编码,将bit序列解释成对应的字符则称之为解码。
这样的一个bit序列可以用于存盘和网络传输等所有只能使用二进制表示的环境中。当需要阅读文件时再解码并在显示屏上显示出人类可识别的字符。
编码是人和机器之间的传递和表示信息的一种方式。
2.编码方式字符与bit序列之间的转换随之带来的两个问题是:
怎么确定每个字符对应的编码?
每个字符的编码长度应该是多少?
第一个问题可以用一个大家公认的标准来解决。
第二个问题则主要是节约的角度考虑,在可以表示特定字符集的情况下,需要用尽可能短的二进制序列。
问题看似简单,但由于历史原因,不同国家不同语种的编码标准并不相同。而且即使同一个标准也在不断的发展。
我们常见的编码标准ASCII,UTF-8,Unicode,GBK(中文编码标准)等。
编码长度由编码方式决定,如ASCII码表示的字符都是一个字节(8 bits),Unicode编码的字符一般用两个字节。
同一种编码中不同字符的编码长度也可能不同。如UTF-8,对字符编码尽量压缩以节俭空间,是一种“可变长编码”。
既然存在这么多种编码方式,那么对于一段经过编码的二进制序列,如果以其他的编码方式解码,显然会得到错误的解码信息。
这就是我们所遇到的乱码问题。
那有没有一种编码方式可以将世界上所有的可表示字符都赋予一个唯一的编码?Unicode便是这样一种编码方式。
但是表示范围包罗万象的一个代价就是字符编码长度的增加。这样数据传输和存放时占用的网络和空间资源就会更多。
UTF-8是在Unicode基础上发展而来的可变长的编码方式。
Character ASCII Unicode UTF-8 GBK0 00110000 00000000 00110000 00110000 00110000
a 01100001 00000000 01100001 01100001 01100001
字 无法表示 01011011 01010111 (u'\u5b57') 11100101 10101101 10010111 ('\xe5\xad\x97') 11010111 11010110 ('\xd7\xd6')
注意:
上面的0是字符0,对应程序中“0”,而不是数字0。
数字是不使用字符编码的,数字可以使用原码、反码和补码表示,在内存中一般使用补码表示,其字节序有大小端模式决定。
3.编码转换不同的编码有各自的特点,下面是一种可能的字符编辑显示、加载传输和存储对应的各阶段编码。
由于Unicode可以和任何编码相互转换,可以借助Unicode实现不同编码之间的变换。
python2中有两种表示字符串的类型:str 和 unicode。basestring是二者的共同基类。
Unicode对象包含的是unicode字符,而str对象包含的是字节(byte)。