重定向到文件时的UnicodeDecodeError

时间:2010-12-28 11:24:46

标签: python unicode

我在Ubuntu终端(编码设置为utf-8)中运行此代码段两次,一次使用./test.py,然后使用./test.py >out.txt

uni = u"\u001A\u0BC3\u1451\U0001D10C"
print uni

没有重定向,它会打印垃圾。通过重定向,我得到了一个UnicodeDecodeError。有人可以解释为什么我只在第二种情况下得到错误,或者甚至更好地详细解释两种情况下幕后发生的事情?

3 个答案:

答案 0 :(得分:246)

答案 1 :(得分:19)

Python在写入终端,文件,管道等时总是编码Unicode字符串。当写入终端时,Python通常可以确定终端的编码并正确使用它。在写入文件或管道时,Python默认为'ascii'编码,除非另有明确说明。通过PYTHONIOENCODING环境变量管道输出时,可以告诉Python该做什么。 shell可以在将Python输出重定向到文件或管道之前设置此变量,以便知道正确的编码。

在您的情况下,您打印了4个不常见的字符,终端不支持其字体。这里有一些例子来帮助解释行为,我的终端实际支持的字符(使用cp437,而不是UTF-8)。

示例1

请注意,#coding注释表示保存源文件的编码。我选择了utf8所以我可以支持我的终端无法支持的源代码。编码重定向到stderr,以便在重定向到文件时可以看到。

#coding: utf8
import sys
uni = u'αßΓπΣσµτΦΘΩδ∞φ'
print >>sys.stderr,sys.stdout.encoding
print uni

输出(直接从终端运行)

cp437
αßΓπΣσµτΦΘΩδ∞φ

Python正确地确定了终端的编码。

输出(重定向到文件)

None
Traceback (most recent call last):
  File "C:\ex.py", line 5, in <module>
    print uni
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-13: ordinal not in range(128)

Python无法确定编码(无),因此使用'ascii'默认值。 ASCII仅支持转换Unicode的前128个字符。

输出(重定向到文件,PYTHONIOENCODING = cp437)

cp437

我的输出文件是正确的:

C:\>type out.txt
αßΓπΣσµτΦΘΩδ∞φ

示例2

现在我将在源码中输入一个我的终端不支持的字符:

#coding: utf8
import sys
uni = u'αßΓπΣσµτΦΘΩδ∞φ马' # added Chinese character at end.
print >>sys.stderr,sys.stdout.encoding
print uni

输出(直接从终端运行)

cp437
Traceback (most recent call last):
  File "C:\ex.py", line 5, in <module>
    print uni
  File "C:\Python26\lib\encodings\cp437.py", line 12, in encode
    return codecs.charmap_encode(input,errors,encoding_map)
UnicodeEncodeError: 'charmap' codec can't encode character u'\u9a6c' in position 14: character maps to <undefined>

我的终端不明白最后一个汉字。

输出(直接运行,PYTHONIOENCODING = 437:替换)

cp437
αßΓπΣσµτΦΘΩδ∞φ?

可以使用编码指定错误处理程序。在这种情况下,未知字符被?替换。 ignorexmlcharrefreplace是其他一些选项。当使用UTF8(支持编码所有Unicode字符)时,将永远不会进行替换,但用于显示字符的 font 仍必须支持它们。

答案 2 :(得分:12)

打印时对其进行编码

uni = u"\u001A\u0BC3\u1451\U0001D10C"
print uni.encode("utf-8")

这是因为当您手动运行脚本时,python会在将其输出到终端之前对其进行编码,当您管道时,python不对其进行编码,因此您在进行I / O时必须手动编码。