【编码】Python编码

今天折腾了半天编码,查了很多Python编码的资料,也试验过了几次,不敢说完全明白,稍微有点感悟,写下此文以加深印象。如有朋友发现错误,请指正。

Python的默认编码

Python2.x的默认编码是Ascii,这个可以从sys模块的getdefaultencoding方法获得:

>>> import sys
>>> sys.getdefaultencoding()
'ascii'

聪明的你可能想到了,既然有setdefaultencoding方法,那就应该有setdefaultencoding吧,于是你敲下以下代码:

>>> sys.setdefaultencoding('utf-8')

但是却发现出现了错误:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute 'setdefaultencoding'

怎么回事,模块没有这个属性?一开始我也不明白,后来发现在python2.5以后版本中,import sys后,sys初始化后会去除setdefaultencoding,解决办法是在import sys后再加一句reload(sys)

>>> import sys
>>> reload(sys)
<module 'sys' (built-in)>
>>> sys.setdefaultencoding('utf-8')
>>>

这里出现了一些奇怪的现象,在python自带的GUI编辑器中,在交互环境下,如果我用了setdefaultencoding方法,后面的代码都不会显示结果了?但是在cmd下却没有这个问题?如果有朋友知道为什么的话,请务必告诉我!

修改默认编码有什么用呢?在decodeencodeunicode方法中,如果不指定编码方式,默认都是默认编码。当然也可以解决程序中出现中文的问题,但是解决这个问题更普遍的方法是在第一行或者第二行加上一句程序编码声明:#coding=utf-8.实际应用中可以两个一起使用。

另外,用setdefaultencoding方法在每次重启后都会恢复,得重新设置。当然也有一劳永逸的方法,就是在python的Lib\site-packages文件夹下新建一个sitecustomize.py,内容为:

# encoding=utf8 

import sys   
reload(sys) 
sys.setdefaultencoding('utf8') 

decode,encode,unicode

首先了解一下什么是unicode编码?为什么要有unicode编码?大家可以在我转载的这篇博文中查看:http://blog.csdn.net/u010262331/article/details/46013905

一般实现不同编码之间的转换,都会借助于unicode,按照以下流程:

str –>unicode –>str
第一步会采用str的decode方法,或者内置的unicode,第二步采用str的encode方法。

decodeencode是str类型的方法,从字面上很容易理解,一个是解码,一个是编码。

decode使用方法:

>>> help(str.decode)
Help on method_descriptor:

decode(...)
    S.decode([encoding[,errors]]) -> object

    Decodes S using the codec registered for encoding. encoding defaults
    to the default encoding. errors may be given to set a different error
    handling scheme. Default is 'strict' meaning that encoding errors raise
    a UnicodeDecodeError. Other possible values are 'ignore' and 'replace'
    as well as any other name registered with codecs.register_error that is
    able to handle UnicodeDecodeErrors.

大概意思就是将字符串编码成unicode对象。比如:

>>> str1 = 'Hello'
>>> str1.decode()
u'Hello'
>>> str1.decode('ascii') 
u'Hello'
>>> str1.decode('utf-8')
u'Hello'

字符串前面的u表示unicode类型

这里的默认编码没改,还是ascii编码,所以第一个decode和第二个decode结果一样可以理解,但是第三个明明是按utf8解码,为什么结果也是一样呢?这个嘛,还是细细看一下遍我刚才给出链接的那篇文章吧。


encode的使用方法:

>>> help(str.encode)
Help on method_descriptor:

encode(...)
    S.encode([encoding[,errors]]) -> object

    Encodes S using the codec registered for encoding. encoding defaults
    to the default encoding. errors may be given to set a different error
    handling scheme. Default is 'strict' meaning that encoding errors raise
    a UnicodeEncodeError. Other possible values are 'ignore', 'replace' and
    'xmlcharrefreplace' as well as any other name registered with
    codecs.register_error that is able to handle UnicodeEncodeErrors.

大概意思就是将字符串(当然也可以使unicode)编码成指定的类型,需要注意的是注意不同编码之间的兼容关系,比如可以把gb2312转成ascii类型,但是不要将ascii转换成gb2312。譬如:

>>> str1.encode('ascii')
'abc'
>>> str1 = 'Hello'
>>> str1.encode('gb2312')
'Hello'
>>> str1.decode()
u'Hello'
>>> str2 = str1.decode()
>>> str2.encode('gb2312')
'Hello'
>>> str2.encode('ascii')
'Hello'

unicode一般如下使用:

>>> str1 = 'Hello'
>>> unicode(str1)
u'Hello'
>>> unicode(str1,'ascii')
u'Hello'
>>> unicode(str1,'gb2312')
u'Hello'

如果不指定编码类型就按默认编码。需要注意的是这里的编码类型是指原来的编码类型。unicode编码也可以在赋值时就确定,不再后续转换,如:

>>> str1 = u'Hello'
>>> str1
u'Hello'

中文编码的问题

你可能已经注意到了,在上面的例子中,我都没有用到中文的例子,这是因为中文编码确实非常坑爹,需要特别说明。首先先看一个例子,有点长,慢慢看:

>>> sys.getdefaultencoding()
'ascii'
>>> a='abc'
>>> b='脚本'
>>> c=u'abc'
>>> d=u'脚本'
>>> e='ab脚本'
>>> f=u'ab脚本'
>>> a.decode()
u'abc'
>>> b.decode()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xbd in position 0: ordinal not in range(128)
>>> c.decode()
u'abc'
>>> d.decode()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)
>>> e.decode()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xbd in position 2: ordinal not in range(128)
>>> f.decode()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 2-3: ordinal not in range(128)
>>> b.decode('gb2312')
u'\u811a\u672c'
>>> a.decode('gb2312')
u'abc'
>>> c.decode('gb2312')
u'abc'
>>> d.decode('gb2312')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)
>>> e.decode('gb2312')
u'ab\u811a\u672c'
>>> f.decode('gb2312')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 2-3: ordinal not in range(128)
>>> unicode(a)
u'abc'
>>> unicode(b)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xbd in position 0: ordinal not in range(128)
>>> unicode(c)
u'abc'
>>> unicode(d)
u'\u811a\u672c'
>>> unicode(e)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xbd in position 2: ordinal not in range(128)
>>> unicode(f)
u'ab\u811a\u672c'
>>> unicode(a,'gb2312')
u'abc'
>>> unicode(b,'gb2312')
u'\u811a\u672c'
>>> unicode(c,'gb2312')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: decoding Unicode is not supported
>>> unicode(d,'gb2312')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: decoding Unicode is not supported
>>> unicode(e,'gb2312')
u'ab\u811a\u672c'
>>> unicode(f,'gb2312')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: decoding Unicode is not supported

如果你看了上面的例子已经明白的话,那就不用再看下面的说明了。例子中的默认编码是ascii。

  • a是一个字母字符串,可以看到a的a.decode()a.dencode('gb2312'),不管是以ascii编码还是gb2312编码(当然也可以用utf-8编码)来解码,都能得到对应的unicode对象。用unicode(a)unicode(a,'gb2312')也是一样的道理;

  • b是一个中文字符串,起初我以为他也会按ascii编码,但是后来发现有点不对劲,我用b.decode()unicode(b)时出错了,报的错还都是一样的UnicodeDecodeError: ‘ascii’ codec can’t decode byte 0xbd in position 0: ordinal not in range(128),意思大概就是ascii真正编码的只有前面127个,但是我们的码却大于127,所以出错了。细究起来,这个涉及到历史原因了。还是老话,去看刚才给出链接的那篇文章。当我指定了编码为gb2312后就正常了b.decode('gb2312'),unicode(b,'gb2312'),输入中文会按gb2312编码我想这可能跟我的系统默认编码有关;

  • c是一个unicode对象,但是同样能够用decode,皆因unicode有容乃大,所以不管我指定那种方式解码,都没有报错,也能得到结果,只不过结果不变而已。如c.decode()c.decode('gb2312')。但是到unicode方法时出现了问题,unicode(c)没错,但是unicode(c,'gb2312')却报了一个TypeError。这个问题我也搞不清楚,有知道的朋友务必告诉我,但是我想在平常中,也不会写到这样的代码吧;

  • d除了内部是中文外,也是一个unicode对象,起初以为会跟c一样,但是却是,在d.decode()d.decode('gb2312')中都报了同样的错,UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128),在unicode方法中跟c完全一样;

  • e的结果跟b是一样的,看来只要在字符串中有中文,编码就是gb2312;

  • f的结果跟d是一样的,看来在unicode对象中只要有中文结果都是一样的;


总结

对以上结果总结一下,有错请指正:

  • 字符串(非unicode)中没有中文,默认编码为ascii(没有修改的话),这种字符串使用范围最广,在decodeunicode中不管有没有指定任何编码都是可以转化成unicode对象的;
  • 当字符串(非unicode)中有中文时,编码就是gb2312(应该是操作系统的默认编码),此时在用decodeunicode时都要指定编码类型;
  • 当字符串是unicode时,也就是在前面有一个u时,如果其中没有中文,是一种情况,可以用decode(指定或不指定),也可以用unicode(只能不指定);
  • 但unicode字符串中含有中文时,只有unicode(不指定时)才没有报错。

建议

  1. unicode对象不再用decodeunicode,如果不确定是否是unicode时,可以在用decodeunicode之前用isinstance(str,unicode) 检测类型。
  2. 遇到中文字符串,用gb2312编码方式解码成unicode再转成需要的编码方式。
posted @ 2015-05-27 22:42  sharpdeep  阅读(119)  评论(0编辑  收藏  举报