1.字符编码的出现
- 因为计算机只能处理数字,如果要处理文本,就必须先把文本转换为数字才能处理。
- 因此,字符编码就是制定一个编码表将字符和编码(可以简单理解为数字)对应起来。字符编码将每一个字符和一个编码唯一对应起来。这样我们就能在计算机上保存字符对应的编码,而在我们查看它时,通过编码表就可以把它对应的字符显示出来。
2.不同的编码方式
里、韩国把韩文编码到Euc-kr 里。各个国家可能都有自己的编码标准,使用不同的编码方式,就可能会出现乱码的情况。下面是几种最常见的编码:
- ASCII编码
最早只有127个字符被编码到计算机里,也就是大小写英文字母、数字和一些符号,这个编码表被称为ASCII编码。
- GB2312和GBK编码
中国制定了GB2312编码,用来把中文编进去,而后有颁布了GBK编码。GB2312是简体汉字编码规范,但GBK是大字符集,不仅包含了简体中文,繁体中文还包括了日语、韩语等所有亚洲文字的双字节字符。
- Unicode字符集
为了解决不同编码造成的乱码问题,Unicode字符集应运而生。Unicode把所有语言都统一到一套编码里,这样就不会再有乱码问题了。Unicode又被称为统一码、万国码。它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。
- UTF-8
如果统一成Unicode编码,乱码问题从此消失了。但是,如果你写的文本基本上全部是英文的话,用Unicode编码比ASCII编码需要多一倍的存储空间,在存储和传输上就十分不划算。本着节约的精神,又出现了把Unicode编码转化为“可变长编码”的UTF-8编码。UTF-8编码把一个Unicode字符根据不同的数字大小编码成1-6个字节,常用的英文字母被编码成1个字节,汉字通常是3个字节,只有很生僻的字符才会被编码成4-6个字节。
3.乱码原因解读
对于不同的编码方式,有一点值得我们注意:
- GB2312、GBK以及UTF-8这几种编码都是兼容ASCII码。这也就是说,当我们对英文字符和数字编码时,不论我们使用哪一种编码方式,都可以正确解读出来。
这也就说明了,对于一个纯英文和数字的文本文件,无论我们使用的是哪一种编码,都不会出现乱码的情况。
- 因此,我们常常遇到的乱码问题,绝大部分都是因为GBK和UTF-8对于中文的编码方式不同。
当我们使用GBK编码保存了一个包含中文的文本文件,再通过UTF-8对这个文件解码时,就会出现乱码。
同样,当我们使用UTF-8编码保存了一个包含中文的文本文件,再通过GBK对这个文件解码时,就会出现乱码。
通过简单的Python代码就可以清晰的看出原因:
print('ABC'.encode('ascii')) # 对'ABC'用ascii编码
print('ABC'.encode('gbk'))
print('ABC'.encode('utf-8'))
# print('你好'.encode('ascii')) 报错,ascii不能编码中文
print('你好'.encode('gbk'))
print('你好'.encode('utf-8'))
输出如图所示:
这个简单的代码,清晰的展示出了:英文字符使用ASCII编码、GBK编码和UTF-8编码的方式相同,不会出现解读错误的情况,也就不会出现乱码。而对于中文字符,gbk中一个汉字编码为两个字节,而utf-8中一个汉字编码为三个字节,两者的编码方式不同。因此,当用GBK解读UTF-8编码的文本或用UTF-8解读GBK编码的文本时,会出现乱码或报错。
4.补充(乱码 or 报错?)
你可能会发现,但你在Python中用一种编码解读另一种编码的文本时,有时会出现乱码,但有时会直接报错。这是为什么呢?
不知道大家在看上面代码的时候,有没有想到一个问题。gbk中一个汉字编码为两个字节,而utf-8中一个汉字编码为三个字节,那么对于一个汉字,你用错编码方式,是一定会报错的。
print('你'.encode('gbk').decode('utf-8')) # 对'你'用gbk编码,再用utf-8解码
print('你'.encode('utf-8').decode('gbk'))
这两条语句都会报错,报错信息就是大家时常会遇到的解码错误:
第一句:
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc4 in position 0: invalid continuation byte
第二句:
UnicodeDecodeError: 'gbk' codec can't decode byte 0xa0 in position 2: incomplete multibyte sequence
一个汉字会报错,那如果我有两个汉字呢?
print('你好'.encode('utf-8').decode('gbk')) # 对'你好'用gbk编码,再用utf-8解码
print('你好'.encode('gbk').decode('utf-8'))
结果如下:
第一条语句解码为了三个汉字,与初始不同,第二条语句报错。看到这里不知道你发现了原因没有?
utf-8中一个汉字编码为三个字节,那么两个汉字就被编码为6个字节,而gbk中一个汉字编码为两个字节,因此使用gbk解码时,这6个字节可以被划分为3的部分,对应3个汉字。
而在第二条语句中,gbk将两个汉字编码为了4个字节,而utf-8是解码不了4个字节的。这也就解释了报错信息后面的:incomplete multibyte sequence(不完整的多字节序列)。
由此,可以得出结论,对于一句中文,当使用gbk编码时,如果编码的字节总数为3的整数倍,就能够用utf-8解码出来,只不过内容不一样。而当使用utf-8编码时,如果编码的字节总数为2的整数倍,就能够用gbk解码出来,只不过内容不一样。