给小白写的“搞懂Python中的编码”

很多初学者会被Python中UnicodeDecodeError弄得一头污水,相信你也一定遇到过这个问题,这里我们讲一下编码相关的知识。(部分内容参考了《编写高质量Python代码的59个有效方法》一书)

1. 原理:

计算机中所有的内容都是用1和0组成的(二进制)。这和计算机的硬件实现原理相关,通常组成计算机的逻辑电路只有接通和断开两个状态,分别用来表示1和0。这样一个逻辑电路组成的位置叫一个比特(bit),一个位置能表示的内容有限,但组合起来,多个一起用就能发挥出无限威力,我们把8位放到一起组成一个字节(Byte)。可以得知,一个字节能表示的最大的整数就是255(二进制11111111=十进制255),字节对应的二进制数字被用来表示大小写英文字母、数字和一些符号,比如65代表大写字母A,122代表小写字母z,这个对应关系就是ASCII编码表,ASCII是American Standard Code for Information Interchange的简称,是为美国英语而设计的。

ASCII 码使用指定的 7 位 或 8 位二进制数组合来表示 128 或 256 种字符。标准ASCII 码也叫基础ASCII码,使用 7 位二进制数(最开始的那1位固定为0)(简称7位ASCII)来表示所有的大写和小写字母,数字0 到9、标点符号, 以及在美式英语中使用的特殊控制字符。

有了这个编码表,在计算机中处理大小英文,数字,标点基本上够用了,但问题是我们的中文有非常多汉字,总数超过了8万,常用的都有3500个之多,更何况世界上还有各种其它语言,那么在计算机中怎么表示数量这么庞大的字符呢?

解决这个问题很简单,每个字符用多个字节来表示,一个不够用两个,两个不够用三个,使用足够多的字节肯定是能表示出来的。使用这种“原始”的多个字节值表示其它的复杂字符,这些8位称为原始8位值。

但问题又来了,各平台,各国家规定都不统一,同样的编码,在一个国家表示某个文字,在另一个国家可能被用来表示另一个文字,在信息交流的时候经常会出现“乱码”。为了解决这个问题,必须得有统一的标准,于是Unicode(统一码、万国码、单一码)就诞生了。Unicode 也称为 UCS(Universal Coded Character Set: 国际编码字符集合) 是一个字符集合。UNICODE标准也在不断发展完善,目前使用 4个字节 表示一个字符可以表示出全世界所有字符。那UNICODE在计算机中如何存储呢,存储时也必须占用4个字节么,这就要涉及到编码的知识。

UNICODE最常见的编码方式是UTF-8,另外还有UTF-16,UTF-32等。如果每个字符都用四个字节来存储,纯英文内容占用空间变成了原来ASCII的四倍,非常浪费空间。而UTF-8编码比较巧妙,采用的是变长的方法,也就是一个字符在UTF-8编码表示时占用1个字节到4个字节不等,兼容ASCII,表示纯英文时,并不会占用更多长度。

2. 转换过程

UTF-8,GBK等,通过解码(decode)得到UNICODE,UNICODE通过编码(encode)可以转换成GBK或UTF8等编码(原始8位值),转换如下图所示。

3. 示例:

u'6啊'.encode('utf8') -> b'6\xe5\x95\x8a'(16进制表示的原始8位值

u'6啊'.encode('gbk') -> b'6\xb0\xa1'(16进制表示的原始8位值

从上面的例子可以看到6是用一个字节表示的,汉字“啊”在utf8中是用三个字节表示的(gbk中是两个字节),其中\xe5意思就是16进制的e5,代表一个8位二进制:

再比如 “涂”这个汉字,unicode 为 u'\u6d82'

使用utf8编码时 '\xe6\xb6\x82'111001101011011010000010 三个8位

使用gbk编码时 '\xcd\xbf'1100110110111111两个8位

4. 结论:

4.1 Unicode可以通过不同的字符编码来实现,最常用的是utf8,它是ASCII码的超集。

4.2 Python 3 有两种表示字符序列的类型:bytes 和 str。bytes 的实例包含原始的8位值,str 的实例包含unicode字符。

4.3 Python 2 也有两种表示字符序列的类型,分别叫做 str 和 unicode。与Python3不同的是,Python2中 str 实例包含原始的8位值;而unicode的实例,则包含unicode字符。Python2里面不够严格,如果str只包含7位ASCII字符,那么unicode和str实例似乎就成了同一种类型:a) 可以相加 b) 可以比较 c) 可以用 %s 格式化。如果使用的时候不注意在特定的场景下就会带来问题。

4.4 在编程时,编码和解码尽量在最外围做,程序处理逻辑中尽量全部使用unicode,当有必要时(比如写入到文件)才encode转换成utf8等编码

从文件等中读取后尽快decode转换成unicode。这样可以大大减少编码带来的问题。

5. 读者思考:

1. utf8为什么这么成功,它是如何兼容ASCII的?

2. 通过上面的示例,是不是发现用gbk存储中文比utf8更省空间,那为什么一般情况下不用gbk来储存,而是用utf8呢?

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20181215G19N6400?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券