Python 输出 Unicode 字符到标准输出时遇到 UnicodeEncodeError

问题

源代码文件(test.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-

print u'中国'

执行

python test.py > output

输出

Traceback (most recent call last):
  File "test.py", line 4, in <module>
    print u'中国'
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)

分析

可能 Python 是按照系统输出编码来输出。

实验 1

源代码文件(test_encoding.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys

print sys.getfilesystemencoding()
print sys.getdefaultencoding()
print sys.stdin.encoding
print sys.stdout.encoding
执行结果

1、直接执行

python test_encoding.py
# UTF-8
# ascii
# UTF-8
# UTF-8

2、直接执行,输出重定向

python test_encoding.py > output; cat output;
# UTF-8
# ascii
# UTF-8
# None

3、按指定本地化(locale)设置执行

查到一篇文档(Overcoming frustration: Correctly using unicode in python2)之后,又学到下面的调用姿势:

LC_ALL=C python test_encoding.py
# ANSI_X3.4-1968
# ascii
# ANSI_X3.4-1968
# ANSI_X3.4-1968

PS:LC_ALL=C python test.py 也会有 UnicodeEncodeError

猜测

是否是这样:如果没有指定标准输出的编码,就使用 sys.getdefaultencoding(),结果导致 ASCII 编码 Unicode 字符出错。

所以继续实验,验证猜测。

实验 2

源代码文件(test2.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys

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

print u'中国'
执行结果

1、直接执行

python test2.py
# 中国

2、直接执行,输出重定向

python test2.py > output; cat output;
# 中国

3、按指定本地化(locale)设置执行

LC_ALL=C python test2.py
# Traceback (most recent call last):
#   File "test2.py", line 9, in <module>
#     print u'中国'
# UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)

基本验证了我的猜测,如果没有指定标准输出的编码,那么使用了默认编码 ASCII。

解决方法

1、定义默认编码

import sys

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

2、重新定义标准输出

import sys
import codecs

sys.stdout = codecs.getwriter('utf8')(sys.stdout)

参考