追逐繁星的孩子

お帰りなさい

首页 标签 归档 分类 关于
Python编码问题记录
日期 2018-04-16   |    标签 Python   |    评论

我个人对于编码问题(尤其在python2中)的理解一直处于半懂不懂的状态,也经常碰到UnicodeEncodeError和UnicodeDecodeError。最近项目中碰到蛮多就恶补了一下,在这里记录总结下吧(个人拙见)!

编码(从编程的角度来讲)。说白了就是人类语言和机器语言的转换过程。编码的过程就是将人类语言转换成机器认识的语言(二进制),而解码则相反。理解这个概念很重要,因为有些新人貌似对这个概念并不清晰。普遍我们所熟知的编码是ascii码,它定义了128个字符所代表的二进制值。但是ascii码仅仅包含了英文字符及一些常见字符,并不能描述其余字符(比如中文、日文、越南语等等)。为了满足需求,诞生了unicode字符集,大家最熟悉的可能就是utf-8编码了(注意:utf-8只是unicode的一种实现方式,具体请上网查询。PS:编码发展史蛮有意思的)。我这边就以utf-8来进行讨论。

python编码问题很大的疑惑点其实在于python版本的差异,python2和python3对于编码的处理不一样。

就比如:

  • 我现在碰到一个关于编码的问题了。
  • 奥,我知道用python3来代替python2。
  • 那么现在就有两个问题了。。

python2使用str类型代表字节序列,使用unicode类型代表unicode字符。注意:字节序列即二进制序列,是机器看的。字符是人类语言,是人看的。那么我们可以这样理解:编码即unicode -> str,解码即 str -> unicode。意思是什么呢?也就是说:我们在python2中定义一个字符串变量 t='abcd' ,这里的 t 就是二进制值。那么如果我输入 t='中' 呢?可想而知,这个'中'字需要使用二进制去表示,python内部会默认将其编码成二进制字节序列进行存储。好的!既然要编码了,我们得采取一种编码方式对吧!python2内置ascii编码进行自动编码(未指定编码时),所以这个语句将会报错。这就是为什么我们在Python2中使用中文字符时需要在py文件头部指定 # -- coding: utf-8 -- 了,为了告诉python解释器,你用这个编码去编解码,而不是默认的ascii码。

那么这里我们可以比较下python3。python3使用bytes类型代表字节序列,使用str代表unicode字符。即python3的str等同于python2的unicode类型,python3的bytes约等于python2的str类型。python3比较好的一点是它默认就支持unicode编码,解释器编译的时候默认就采用了utf8去编解码。这样我们进行编程的时候无需再和字节序列去打交道,减少编程bug。

这样其实就可以解释为什么大家都推荐python3去代替python2处理编码问题了。

接下来我们来看下编解码的相关报错原因:UnicodeEncodeError和UnicodeDecodeError。这两个报错我们应该不会陌生。

python2为str和uniode提供了相应的编解码方法:str.decode(),unicode.encode()。字节需要解码,字符需要编码。对的。那么我们常见的UnicodeEncodeError和UnicodeDecodeError会出现在什么时候呢?

    # -*- coding: utf-8 -*-
    # 指定默认utf8编译,要不然'中'根本过不了编译。想想为什么?
    'a'.decode().encode() #正确
    '中'.decode() #报错 UnicodeDecodeError
    '中'.decode('utf8') #正确
    '中'.decode('utf8').encode() #报错 UnicodeEncodeError
    '中'.decode('utf8').encode('utf8') #正确

上面的代码很容易看出来:由于python2默认使用ascii进行编解码,所以当对非ascii字符进行默认解码时会出现UnicodeDecodeError,因为现在这个'中'其实就是一串经过utf8编码的字节序列,ascii根本不认识它。就会解码报错。UnicodeEncodeError报错同理。所以我们知道了,我们应该保证使用的编码是同一个。

其实python2中str除了decode()还有encode(),unicode除了encode()也有decode()。why?字节序列还能编码?其实这个str.encode()其实是用于编码其他的字节序列表示,比如base64。就比如 '\xe4\xb8\xad'.encode('base64')。当然这也从另一方面验证了python2的编码真的很fuck。当然python3已经去掉了相应的方法。只保留unicode.encode()和bytes.decode()

其实编码问题可以延伸到很广,比如编码还有很多('GBK','GB2312','lantai-1'等等),再比如window默认编码,控制台默认编码,linux上默认编码,文件编码等等。这些都是容易出现编码问题的点。当然我们了解了基本的编码逻辑这些都是可以去解释的,对吧?

其实我们很多时候并不会碰到编码问题,比如python中,很多库或者框架都替我们很好的处理了字节字符的转换。但是这样其实有利有弊,当我们无法碰到问题的时候我们就不会去研究这些问题背后的原理。等到正在遇到的时候就会无从下手。再者字节、字符、编码这些概念其实算是计算机行业的常识性知识。有利于更好的了解计算机系统对吧!

Pythonic code! Pythonic life!