2

经过几次测试后,我设法在下面的最小 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)

这是问题:

为什么 python 在打印 unicode 字符串时的行为不一样,无论它的标准输出是到控制台还是被重定向或通过管道传输到其他东西?

4

2 回答 2

4

首先,ISO-8859-1不是有效的编码声明。你想要iso-8859-1。如果您查看文档,您可以将其称为latin_1, iso-8859-1, iso8859-1, 8859, cp819, latin, latin1, or L1,但不是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. */

但是源文件解码不会经过相同的编解码器查找过程。因为它发生在编译时而不是运行时,所以没有理由这样做。(无论如何,说“它似乎有效,即使文档说它是错误的......那为什么它不能完全正确?”首先有点愚蠢。)

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

坏代码.py:

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

好代码.py:

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

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

现在,为什么它在控制台时“工作”但在管道时引发异常?

好吧,当您打印到 Windows 控制台或 Unix TTY 时,Python 有一些代码可以尝试猜测要使用的正确编码。(我不确定在 Windows 上会发生什么;据我所知,它甚至可能使用 UTF-16 输出。)当您不打印到控制台/TTY 时,它不能这样做,所以您必须明确指定编码。

您可以通过查看 、 和 来了解正在发生的sys.stdout.isatty()一些sys.stdout.encoding事情sys.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(meaning ascii),并且 (我认为 - 我必须检查代码) 基于getdefaultencoding()3.x 中的某些内容。这意味着如果您尝试stdout在 2.x 中不是 TTY 时打印 Unicode,它将尝试将其编码为ascii, strict,如果您有非 ASCII 字符,这将失败。

如果您以某种方式知道要使用哪种编解码器,则可以在打印时通过检查isatty()并编码到该编解码器(或者甚至是asciiignore而不是strict,如果您愿意)手动处理此问题,而不是尝试打印 Unicode。(如果你知道你想要什么编解码器,你甚至可能希望在 3.x 中这样做——如果你试图生成 Windows-1252 文件,默认为 UTF-8 并没有太大帮助……)

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

无码.py:

print u"V\xe9rifier l'affichage de cette cha\xeene"
print u"V\u00e9rifier l'affichage de cette cha\u00eene"

我为我的 Mac 终端获取了编码为 UTF-8 的 Unicode 字符串,并且(显然)为我的 Windows cmd 窗口获取了 Windows-1252,但异常重定向到一个文件。

于 2012-12-17T23:00:22.650 回答
0

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

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)。

于 2015-02-01T19:04:25.737 回答