格式化固定宽度的字符串(unicode和utf8)

时间:2017-08-20 14:11:20

标签: python linux python-2.7

我需要以类似表的格式解析并输出一些数据。输入采用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
αβγδ

如何在两种情况下强制它正常工作?

3 个答案:

答案 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(查看thisthis SO问题):

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

现在print将正确输出strunicode个对象,因为基础流编写器会在运行时将它们转换为UTF-8

输出前的手动字符串编码

当然,您也可以在输出之前手动将每个unicode编码为UTF-8 str

print ('%5s' % s2).encode('utf8')

但这很乏味且容易出错。

显式文件打开

为了完整性:在Python 2中打开使用特定编码(如UTF-8)进行写入的文件时,您应该使用io.opencodecs.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')