本节书摘来自异步社区《Python Cookbook(第2版)中文版》一书中的第1章,第1.22节,作者[美]Alex Martelli , Anna Martelli Ravenscrof , David Ascher ,高铁军 译,更多章节内容可以访问云栖社区“异步社区”公众号查看。
1.22 在标准输出中打印Unicode字符
任务
你想将Unicode字符串打印到标准输出中(比如为了调试),但是这些字符串并不符合默认的编码。
解决方案
通过Python标准库中的codecs模块,将sys.stdout流用转换器包装起来。比如,如果你知道输出会被打印到一个终端,而且该终端以ISO-8859-1的编码显式字符,可以这样编写代码:
import codecs, sys
sys.stdout = codecs.lookup('iso8859-1')[-1](sys.stdout)
讨论
Unicode涵盖极广,全世界的语言字符都在Unicode的表示范围之内,另外,Unicode字符串的内部表示也与Unicode使用者没有关系。一个用于处理字节的文件流,比如sys.stdout,都有自己的编码。可以通过修改site模块改变其默认的编码,该文件流将对新文件使用新编码。不过,这样也需要完全改变你的Python安装,而且其他一些程序则可能会被搞乱,它们依然会按照你原先的编码设置工作(一般是典型的Python标准编码,ASCII)。因此,这种修改并不值得推荐。
本节的方法则用了一个技巧:将sys.stdout绑定到一个使用Unicode输入和ISO-8859-1(也就是Latin-1)输出的流。这种方法并不改变之前sys.stdout上的任何编码,如下面代码所示。首先,我们用一个变量指向原来的基于ASCII的sys.stdout:
>>> old = sys.stdout
然后,我们可以创建一个Unicode字符串,这个字符串通常情况下是不能通过sys.stdout输出的:
>>> char = u"\N{LATIN SMALL LETTER A WITH DIAERESIS}"
>>> print char
Traceback (most recent call last):
File "<stdin>", line 1, in ?
UnicodeError: ASCII encoding error: ordinal not in range(128)
如果这个操作没有出现错误,那是因为Python认为它知道你的“终端”用了什么编码(特别是,如果你的“终端”是IDLE—Python所附的免费的开发环境,Python极有可能能够确认正确的编码)。如果出现了错误,或者没有提示错误,但是输出的字符却不是你期望的,那是因为你的“终端”使用了UTF-8编码,而Python却不知道。如果属于后者的情况,可以用codecs流对sys.stdout进行包装以解决UTF-8编码问题,将sys.stdout绑定到被封装过的流,然后重新试一次:
>>> sys.stdout = codecs.lookup('utf-8')[-1](sys.stdout)
>>> print char
ä
这个方法只在你的“终端”、终端模拟器或者其他类型的交互式Python解释窗口支持UTF-8编码时才有效,而且具有极强的字符表现力,能够显示出任何需要的字符。如果没有这样的程序或设备,可以在因特网上找一个适用于你的平台的免费的程序。
Python会尝试确认你的“终端”的编码,并把编码的名字存在sys.stdout.encoding中作为一个属性。有时(但不是总是),它能够判断出正确的编码。IDLE已经对sys.stdout进行了包装,正如本节解决方案的方法一样,所以,在Python的交互式环境之下,可以直接打印出Unicode字符串。