我需要以类似表的格式解析并输出一些数据。输入采用unicode编码。这是测试脚本:
#!/usr/bin/env python
s1 = u'abcd'
s2 = u'\u03b1\u03b2\u03b3\u03b4'
print '1234567890'
print '%5s' % s1
print '%5s' % s2
在test.py
:
1234567890 abcd αβγδ
但如果我尝试将输出重定向到文件test.py > a.txt
,我会收到错误:
Traceback (most recent call last): File "./test.py", line 8, in print '%5s' % s2 UnicodeEncodeError: 'ascii' codec can't encode characters in position 1-4: ordinal not in range(128)
如果我将字符串转换为UTF-8编码,例如s2.encode('utf8')
重定向工作正常,但数据位置被破坏:
1234567890 abcd αβγδ
如何在两种情况下强制它正常工作?
答案 0 :(得分:3)
归结为输出流编码。在这种特殊情况下,由于您使用的是print
,因此使用的输出文件为sys.stdout
。
stdout
未重定向当您以交互模式运行Python时,或者当您不将stdout
重定向到文件时,Python会根据环境使用编码,即区域设置环境变量,如LC_CTYPE
。例如,如果你运行你的程序:
$ LC_CTYPE='en_US' python test.py
...
UnicodeEncodeError: 'ascii' codec can't encode characters in position 1-4: ordinal not in range(128)
它将ANSI_X3.4-1968
用于sys.stdout
(请参阅sys.stdout.encoding
)并失败。但是,您使用的是UTF-8
(正如您显而易见的那样):
$ LC_CTYPE='en_US.UTF-8' python test.py
1234567890
abcd
αβγδ
您将获得预期的输出。
stdout
重定向到文件当您将stdout
重定向到文件时,Python不会尝试从您的环境区域设置中检测编码,但会检查另一个环境变量PYTHONIOENCODING
(请查看来源,initstdio()
in Python/pylifecycle.c
)。例如,这将按预期工作:
$ PYTHONIOENCODING=utf-8 python test.py >/tmp/output
因为Python会对UTF-8
文件使用/tmp/output
编码。
stdout
编码覆盖您也可以使用所需的编码手动重新打开sys.stdout
(查看this和this SO问题):
import sys
import codecs
sys.stdout = codecs.getwriter('utf8')(sys.stdout)
现在print
将正确输出str
和unicode
个对象,因为基础流编写器会在运行时将它们转换为UTF-8
。
当然,您也可以在输出之前手动将每个unicode
编码为UTF-8
str
:
print ('%5s' % s2).encode('utf8')
但这很乏味且容易出错。
为了完整性:在Python 2中打开使用特定编码(如UTF-8)进行写入的文件时,您应该使用io.open
或codecs.open
,因为它们允许您指定编码(请参阅this question),与内置open
:
from codecs import open
myfile = open('filename', encoding='utf-8')
或:
from io import open
myfile = open('filename', encoding='utf-8')
答案 1 :(得分:2)
您应该将'%5s' % s2
编码为s2
。因此,以下内容将具有预期的输出:
print ('%5s' % s2).encode('utf8')
答案 2 :(得分:1)
print '%5s' % s1
是正确的,但print '%5s' % s2
不正确。必须print ('%5s' % s2).encode('utf8')
试试这段代码。
#!/usr/bin/env python
s1 = u'abcd'
s2 = u'\u03b1\u03b2\u03b3\u03b4'
print '1234567890'
print '%5s' % s1
print ('%5s' % s2).encode('utf8')