关于python编解码的一些坑(二)

学过python的都知道,python的encode,decode里面有一些坑,掉进去后比较难爬出来。正好这段时间想总结一下这些坑,我会写2-3篇文章来介绍我对这些坑的理解。既然是个人理解,那很可能有些考虑不对的地方。因此如果大家自认为有更准确的理解,也希望能相互交流,共同学习,一起进步!另,文章中引用的文献,我都做了说明,也都注明了出处,以方便大家查阅。

本篇文章是关于encoding:utf8sys.setdefaultencoding(‘utf-8’) 的个人理解

开文明义,我首先抛出我对于这两个编码声明的理解:
“#encoding:utf8”
的作用是“显式”地设置字符串的编码方式为utf8


import sys
reload(sys)
sys.setdefaultencoding(‘utf-8’)
的作用是“隐式”地设置字符串的编码方式 为utf8

那么什么是“显示”,什么是“隐式”
显式 分这么几种情况:
1) 如果代码中有中文注释,就需要此声明
2) 字符串没有显示地调用encode时,实质上也是编码为了字节码,编码的形式也是这里的“显式”编码。如:

#encoding:utf8
import sys
print "defaultencoding is: %s"%sys.getdefaultencoding()
s1="你好"
print repr(s1)
s2=u"你好"
print repr(s2.encode('utf8'))

结果(关于repr函数请自行查阅相关资料):

defaultencoding is: ascii
'\xe4\xbd\xa0\xe5\xa5\xbd'
'\xe4\xbd\xa0\xe5\xa5\xbd'

可以发现s1也是按照utf8进行了编码,与unicode字符串s2的显式地指明编码方式的结果是一样的 。(注意此时的默认编码为ascii)

那么什么叫隐式呢?
我归纳起来有这几种
1) 将本是字节码字节码串继续再按另外一种编码方式encode为新的字节码串时,如下图:

这里写图片描述

代码如下:

#encoding:utf8
import sys
print "defaultencoding is: %s"%sys.getdefaultencoding()
s1="你好"
s2=s1.encode('gbk')

结果:

defaultencoding is: ascii
Traceback (most recent call last):File "encode.py", line 5, in <module> s2=s1.encode('gbk')
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)

为什么会出现这种情况:
因为 Python 会自动的先将 s 解码为 unicode ,然后再编码成 gb18030。因为解码是python自动进行的,我们没有指明解码方式的情况下,python 就会使用 sys.defaultencoding 指明的方式来解码。很多情况下 sys.defaultencoding 是 ascii,本例就是这种情况。如果 s 不是这个类型就会出错。这里就是所谓的“隐式”编码!
那应该如何处理呢?有两种处理方式,如下:
一是明确的指示出 s 的编码方式

# -*- coding: utf-8 -*- 
s = '你好' 
s.decode('utf-8').encode('gbk') 

二是更改 sys.defaultencoding 为文件的编码方式

# -*- coding: utf-8 -*- 
import sys 
reload(sys) 
sys.setdefaultencoding('utf-8') 
s = '你好' 
str.encode('gbk')

2) “==” 的情况下,也是采用了“隐式编码”,借用
http://blog.ernest.me/post/python-setdefaultencoding-unicode-bytes 的例子。
(斜字体引用的是链接中的内容)
dictionray 行为异常
假设我们要从一个 dictionary 里查找一个 key 是否存在,通常来说,有两种可行方法。

#-*- coding: utf-8 -*-
d = {1:2, '1':'2', '你好': 'hello'}
def key_in_dict(key)
    if key in d:
        return True
    return False

def key_found_in_dict(key):
    for _key in d:
        if _key == key:
            return True
    return False

我们对比下改变系统默认编码前后这俩函数的输出有什么不同。

#-*- coding: utf-8 -*-

print(key_in_dict('你好'))
print(key_found_dict('你好'))
print(key_in_dict(u'你好'))
print(key_found_in_dict(u'你好'))

print('------utf-8------')

import sys
reload(sys)
sys.setdefaultencoding('utf-8')

print(key_in_dict('你好'))
print(key_found_dict('你好'))
print(key_in_dict(u'你好'))
print(key_found_in_dict(u'你好'))

输出:

$~ True
$~ True
$~ False
$~ False
$~ ------utf-8------
$~ True
$~ True
$~ False
$~ True

可以看到,当默认编码改了之后,两个函数的输出不再一致。
dict 的 in 操作符将键做哈希,并比较哈希值判断是否相等。对于 ascii 集合内的字符来说,不管是字节字符类型还是还是 unicode 类型,其哈希值是一样的,如 u’1’ in {‘1’:1} 会返回 True,而超出 ascii 码集的字符,如上例中的 ‘你好’,它的字节字符类型的哈希与 unicode 类型的哈希是不一样的。
而 == 操作符则是做了一次转换,将字节字符(byte string,上面的 ‘你好’)转换成 unicode(u’你好’) 类型,然后对转换后的结果做比较。在 ascii 系统默认编码中,’你好’转换成 Unicode 会产生 Warning: UnicodeWarning: Unicode equal comparison failed to convert both arguments to Unicode - interpreting them as being unequal,因为超出码集无法转换,系统会默认其不相等。当系统编码被我们手动改为 utf-8 后,这个禁忌则被解除,’你好’ 能够顺利被转换成 unicode,最后的结果就是,in 和 == 行为不再一致。**

看到这两个例子,是否会让你对编码错误产生“防不胜防”的感觉,反正我是有这种感觉。实质上,对python2.x产生的类似这种错误:

UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)

我们是有办法完全避免的(虽然这种办法也并不提倡),如下:

# encoding:utf8
import sys
reload(sys)
sys.setdefaultencoding('utf8')

即保证“显示”编码和“隐式”编码一致即可!

但是,更好的方案其实是要保证一个良好的编码风格,借用“http://blog.ernest.me/post/python-setdefaultencoding-unicode-bytes”中的结论:
对python2.x而言:
o 所有 text string 都应该是 unicode 类型,而不是 str,如果你在操作 text,而类型却是 str,那就是在制造 bug。
o 在需要转换的时候,显式转换。从字节解码成文本,用 var.decode(encoding),从文本编码成字节,用 var.encode(encoding)。
o 从外部读取数据时,默认它是字节,然后 decode 成需要的文本;同样的,当需要向外部发送文本时,encode 成字节再发送。

本文参考文献:
http://blog.ernest.me/post/python-setdefaultencoding-unicode-bytes
http://vb2005xu.iteye.com/blog/258633

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值