为什么在控制台和管道中打印unicode字符串时python的行为不一样?

时间:2012-12-17 22:33:59

标签: python encoding stdout

经过几次测试后,我设法限制了下面的最小test.py脚本中的错误功能:

# -*- coding: iso-8859-1 -*-
print u"Vérifier l'affichage de cette chaîne"

注意:test.py以ISO-8859-1(即latin-1)编码,即“é”等于“\ xe9”,“î”等于“\ xee”

D:\test>python --version
Python 2.7.3
D:\test>python test.py
Vérifier l'affichage de cette chaîne
D:\test>python test.py > test.log
Traceback (most recent call last):
  File "test.py", line 2, in <module>
    print u"VÚrifier l'affichage de cette cha¯ne"
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe9' in position 1: ordinal not in range(128)

以下是问题:

在打印unicode字符串时,如果python的标准输出是转到控制台还是重定向或管道传输到其他字符串,那么python的行为是什么?

2 个答案:

答案 0 :(得分:4)

首先,ISO-8859-1不是有效的编码声明。你想要iso-8859-1。如果您查看the docs,可以拨打latin_1iso-8859-1iso8859-18859cp819latinlatin1L1,但不是ISO-8859-1

看起来codecs.lookup向后弯曲以接受错误输入,包括执行不区分大小写的查找。如果您追踪codecs.lookup_codecs.lookup_PyCodec_Lookup,则可以看到此评论:

/* Convert the encoding to a normalized Python string: all
   characters are converted to lower case, spaces and hyphens are
   replaced with underscores. */

但是源文件解码并没有经过相同的编解码器查找过程。因为它发生在编译时而不是运行时,所以没有理由这样做。 (无论如何,说&#34;它似乎有效,即使文档说它错了......那么为什么它不能正常工作?&#34;在第一个是愚蠢的的地方。)

要演示,如果我创建两个Latin-1文件:

badcode.py:

# -*- coding: ISO-8859-1 -*-
print u"Vérifier l'affichage de cette chaîne"

goodcode.py:

# -*- coding: iso-8859-1 -*-
print u"Vérifier l'affichage de cette chaîne"

第一个失败,第二个失败。

现在,为什么它&#34;工作&#34;什么时候它会进入控制台,但在管道输出时引发异常?

好吧,当你打印到Windows控制台或Unix TTY时,Python有一些代码试图猜测要使用的正确编码。 (我不确定Windows下的内容会发生什么;它甚至可能使用UTF-16输出,据我所知。)当你不打印到控制台/ TTY时,它可以&#39 ; t执行此操作,因此您必须明确指定编码。

通过查看sys.stdout.isatty()sys.stdout.encodingsys.getdefaultencoding(),您可以看到一些正在发生的事情。这是我在不同情况下在Mac上看到的内容:

  • Python 2,无重定向:True, UTF-8, ascii, Vérifier
  • Python 3,无重定向:True, UTF-8, utf-8, Vérifier
  • Python 2,重定向:False, None, ascii, UnicodeEncodeError
  • Python 3,重定向:False, UTF-8, utf-8, Vérifier

如果isatty()encoding将是TTY的适当编码;否则,encoding将成为默认值,即2.x中的None(意为ascii),并且(我认为 - 我必须检查代码)基于3.x中的getdefaultencoding()。这意味着,如果您尝试打印Unicode而stdout不是2.x中的TTY,它会尝试将其编码为asciistrict,如果您&#39将失败;有非ASCII字符。

如果您知道要使用哪种编解码器,可以通过检查isatty()和编码到该编解码器(甚至asciiignore而不是{ {1}},如果您愿意,每当您打印时,而不是尝试打印Unicode。 (如果你知道你想要什么编解码器,你甚至可能想要这样做3.x-默认为UTF-8如果你试图生成Windows-1252文件那么太有帮助... )

那里的差异实际上与Latin-1无关。试试这个:

nocode.py:

strict

我的Mac终端编码为UTF-8的Unicode字符串,以及(显然)Windows-1252到我的Windows cmd窗口,但异常重定向到文件。

答案 1 :(得分:0)

因为我来到这里寻找“不要聪明”切换到python的print(),答案提供了只读变量的提示,这里是“make python相信stdout可以处理utf-8”片段:< / p>

import sys, codecs

# somewhere in the function you need it or global main():
sys.stdout = codecs.open('/dev/stdout', encoding='utf-8', mode='w', errors='strict')

在那里,现在python并不关心它是tty,tee(1),文件重定向还是只是cat(1)。