首先,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()并编码到该编解码器(或者甚至是ascii,ignore而不是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,但异常重定向到一个文件。