Python print 字符串编码问题

又一次, 被Python的编码问题搞得焦头烂额. 过去的记忆中, Python给我的印象之一就是让人痛不欲生的编码问题, 后来基本不碰Python很可能与此有关. 这次又用到Python了, 基本上, 整个过程还是比较顺畅的, Python在某些问题上面的确有优势. 但是不可避免的又碰到编码问题.

编码问题是那种你希望永远不要碰到, 而一旦碰到就会很头疼的一类问题. 是一种在解决实际问题过程中不可避免会碰到的不愉快的小插曲之一.

事情是这样, 在Python中调用win32的api, 枚举所有活动的窗口, 得到窗口的句柄, 窗口标题, 和窗口类名, 拿到这些数据之后, 用print 输出到控制台中. 开始一切都很好, 突然某次在运行的时候出错: UnicodeEncodeError, 但是之前都没有报过错, 而且接下来问题时有时无.

原因在于, Chrome浏览器窗口的标题是可以包含特殊字符的, 这里面的文本可能是任何东西, 有些时候标题中的字符都能用gbk表示, 有时候就会包含特殊字符.

例如, 打开某个页面, 碰巧标题是下面的字符串:

 
how to create a windows service in python « Python recipes « ActiveState Code
 

这里面的字符«就是unicode字符, 在gbk中是不存在的, 在HTML用

 
«
 

来表示.

如果在cmd中运行脚本, print到控制台中会抛出UnicodeEncodeError异常. 因为默认cmd里面的编码是gbk, print要想输出则必须先将unicode转换为gbk, 然而gbk里面没有某些unicode字符的对应字符, 结果就抛出异常:

 
UnicodeEncodeError: 'gbk' codec can't encode character '\xab' in position 42: illegal multibyte sequence
 

来分析一下原因, 首先, 从窗口中取得的标题字符串是unicode的, 那么print函数要将其输出到控制台, 必须变成控制台的编码, 在windows里, 控制台的编码就是gbk. 在print函数内部必然要用encode将unicode变成gbk, 如果unicode中包含了gbk所不能表示是字符, 则根据error handling的值决定下一步动作, 而Python的默认error handling是'strict', 也就是当不能encode的时候抛出异常. 这就是事情的全过程.

这一点Python处理的很特别, 按照一般的原则, 像编码解码这样的东西, 即使有问题, 无非乱码而已, 而Python却一定要报错, 现在整个脚本因为这个并不重要的因素而无法运行了.

其实像这种场景, print函数即使输出乱码, 很多时候并不会影响整个脚本的功能, 一般的愿望是希望脚本正常运行下去.

这实际也是大部分平台的处理方式.

那么怎样即能够输出又不影响阅读呢?

这里推荐一种方法

 
print ("title: " , unicode_str.encode('gbk', 'backslashreplace').decode('gbk', 'backslashreplace'))
 

输出结果是

 
title:  how to create a windows service in python \xab Python recipes \xab ActiveState Code - Google Chrome
 

先encode, 用backslashreplace处理特殊字符, 完成之后得到的字节流中特殊字符被转义字符取代了, 然后再decode为unicode, 该unicode交给print的时候, 其中的特殊字符已经变成可以用gbk表示的东西了, 其实就是用对应的数字.

其实这本来应该是我预想中的应有的默认行为, 但是现在必须自己手动处理. 更何况处理的手段还非常的ugly, 如果碰到大量的地方需要修改, 又或者需要print复合数据结构, 例如print list 或者dictionary, 或者pair之类的. 必须找更好的解决办法, 例如改变系统encode的时候的默认error handling, 不要用'strict'. 既然有默认配置, 按理应该是可以修改的, 不过还不知道怎么去做, 也找不到相关的文档.