在Python2中,混合使用Unicodestr类型的字符串经常会碰到类似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:

  1. http://stackoverflow.com/questions/2596714/why-does-python-print-unicode-characters-when-the-default-encoding-is-ascii
  2. https://docs.python.org/2/howto/unicode.html
  3. http://stackoverflow.com/questions/2081640/what-exactly-do-u-and-r-string-flags-do-in-python-and-what-are-raw-string-l
  4. http://blog.csdn.net/trochiluses/article/details/16825269