Python2的编码问题
在Python2中,混合使用Unicode
和str
类型的字符串经常会碰到类似UnicodeEncodeError: 'ascii' codec can't encode character u'\xfc' in position 0: ordinal not in range(128)
的错误。比如说如下的操作:
print u'ü' # It's OK
print 'ü' # It's OK
print 'ü' + u'ü' # Raise UnicodeDecodeError
上述错误的原因在于,Python在执行unicode类型的对象和str类型的对象相加时,首先会将str类型的对象转换为unicode类型的对象,再进行相加操作。而Python在转换时默认使用编码是ascii的,所以在转换时发生了错误而抛出异常。
以下,我们通过显式地指定转换的编码就可以得到正确的结果了。
print 'ü'.decode('utf8')+ u'ü' # It's OK
print 'ü'+ u'ü'.encode('utf8') # It's OK
问:上述两行相加操作的得到的对象是一样的吗(这题目可以当面试题了)?
答案是No,前者得到了是一个unicode类型对象,而后者得到的是str类型的对象。验证如下:
print type('ü'.decode('utf8')) # <type 'unicode'>
print type(u'ü'.encode('utf8')) # <type 'str'>
print type('a' + u'a') # <type 'unicode'>
问:在引号前面加上u
到底发生了什么?u'ü'
是否相当于unicode('ü')
?
答案是u'ü'
相当于unicode('ü', 'utf-8')
,并不相当于unicode('ü')
(unicode的编码默认是ascii的)。验证如下:
repr(unicode('ü', 'utf-8')) == repr(u'ü') # True
repr(unicode('ü')) == repr(u'ü') # Raise UnicodeDecodeError
问:上述的’utf-8’是从何而来的?为什么是’utf-8’而不是’utf-16’或’ascii’?
答案是Python会使用sys.stdout.encoding
中设置的编码格式来作为其编解码的编码格式,而sys.stdout.encoding
会读取bash的环境变量LC_CTYPE
,而上述代码运行时的这个环境变量的值为”UTF-8”。验证代码如下:
-
设定
LC_CTYPE
为UTF-8并运行下面的代码(结果在旁边comment里):import sys print sys.stdout.encoding # UTF-8 print u'ü' # ü
-
设定
LC_CTYPE
为US-ASCII并运行下面的代码:import sys print sys.stdout.encoding # US-ASCII print u'ü' # raise UnicodeEncodeError
问:encode和decode有啥区别?为啥有的地方用encode方法,有的地方用decode方法?
答案是这只是个叫法而已,不需要太纠结,从A到B叫encode,那么从B到A自然就叫decode了。在Python里这里的A就是unicode,B就是str。
问:那str.encode()和unicode.decode()该如何理解?
这个问题比较tricky,Python在做str.encode时会做一个隐式的类型转换,将str类型转换为unicode类型再做encode(由于这个转换默认是用ascii编码,所以很多情况下会报UnicodeDecodeError)。而unicode.decode()也是一样的(先转换为str再做decode)。因此,在使用时我们大可忽略这两个方法而只去使用unicode.encode和str.decode(explicit is better than implict)。验证代码:
import sys
print sys.getdefaultencoding() # ascii
# The below two expressions are the same
print 'ü'.encode('utf-8') # raise UnicodeDecodeError
print 'ü'.decode(sys.getdefaultencoding()).encode('utf-8') # raise UnicodeDecodeError
# The below two expressions are the same
print u'ü'.decode('utf-8') # raise UnicodeEncodeError
print u'ü'.encode(sys.getdefaultencoding()).decode('utf-8') # raise UnicodeEncodeError
Reference:
- http://stackoverflow.com/questions/2596714/why-does-python-print-unicode-characters-when-the-default-encoding-is-ascii
- https://docs.python.org/2/howto/unicode.html
- http://stackoverflow.com/questions/2081640/what-exactly-do-u-and-r-string-flags-do-in-python-and-what-are-raw-string-l
- http://blog.csdn.net/trochiluses/article/details/16825269
Comments