在Python中管道stdout时设置正确的编码

时间:2009-01-29 16:58:00

标签: python encoding terminal stdout python-2.x

在管道Python程序的输出时,Python解释器会对编码感到困惑,并将其设置为None。这意味着这样的程序:

# -*- coding: utf-8 -*-
print u"åäö"

在正常运行时可以正常工作,但在以下情况下失败:

  

UnicodeEncodeError:'ascii'编解码器无法对位置0中的字符u'\ xa0'进行编码:序号不在范围内(128)

在管道序列中使用时。

管道时使这项工作的最佳方法是什么?我可以告诉它使用shell / filesystem /无论使用什么编码吗?

我到目前为止看到的建议是直接修改你的site.py,或者使用这个hack硬编码defaultencoding:

# -*- coding: utf-8 -*-
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
print u"åäö"

有没有更好的方法让管道工作?

11 个答案:

答案 0 :(得分:166)

首先,关于这个解决方案:

# -*- coding: utf-8 -*-
print u"åäö".encode('utf-8')

每次使用给定的编码明确打印是不切实际的。这将是重复且容易出错的。

更好的解决方案是在程序开始时更改 sys.stdout ,以使用选定的编码进行编码。这是我在Python: How is sys.stdout.encoding chosen?找到的一个解决方案,特别是“toka”的评论:

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

答案 1 :(得分:151)

您的代码在脚本中运行时有效,因为Python将输出编码为您的终端应用程序正在使用的任何编码。如果你是管道,你必须自己编码。

经验法则是:始终在内部使用Unicode。解码您收到的内容,并对您发送的内容进行编码。

# -*- coding: utf-8 -*-
print u"åäö".encode('utf-8')

另一个教学示例是一个Python程序,用于在ISO-8859-1和UTF-8之间进行转换,使得所有内容都处于大写状态。

import sys
for line in sys.stdin:
    # Decode what you receive:
    line = line.decode('iso8859-1')

    # Work with Unicode internally:
    line = line.upper()

    # Encode what you send:
    line = line.encode('utf-8')
    sys.stdout.write(line)

设置系统默认编码是个坏主意,因为您使用的某些模块和库可能依赖于它是ASCII。不要这样做。

答案 2 :(得分:120)

您可能想尝试将环境变量“PYTHONIOENCODING”更改为“utf_8”。我写了page on my ordeal with this problem

博客文章博士:

import sys, locale, os
print(sys.stdout.encoding)
print(sys.stdout.isatty())
print(locale.getpreferredencoding())
print(sys.getfilesystemencoding())
print(os.environ["PYTHONIOENCODING"])
print(chr(246), chr(9786), chr(9787))

给你

utf_8
False
ANSI_X3.4-1968
ascii
utf_8
ö ☺ ☻

答案 3 :(得分:60)

export PYTHONIOENCODING=utf-8

完成这项工作,但不能在python本身设置它......

我们可以做的是验证是否设置并告诉用户在调用脚本之前设置它:

if __name__ == '__main__':
    if (sys.stdout.encoding is None):
        print >> sys.stderr, "please set python env PYTHONIOENCODING=UTF-8, example: export PYTHONIOENCODING=UTF-8, when write to stdout."
        exit(1)

更新以回复评论: 管道到标准输出时问题就存在了。 我在Fedora 25 Python 2.7.13中进行了测试

python --version
Python 2.7.13

cat b.py

#!/usr/bin/env python
#-*- coding: utf-8 -*-
import sys

print sys.stdout.encoding

正在运行./b.py

UTF-8

运行./b.py |少

None

答案 4 :(得分:5)

我有similar issue last week。在我的IDE(PyCharm)中很容易修复。

这是我的修复:

从PyCharm菜单栏开始:文件 - >设置... - >编辑 - >文件编码,然后设置:“IDE编码”,“项目编码”和“属性文件的默认编码”ALL到UTF-8,她现在就像一个魅力。

希望这有帮助!

答案 5 :(得分:4)

Craig McQueen的回答是一个有争议的消毒版本。

import sys, codecs
class EncodedOut:
    def __init__(self, enc):
        self.enc = enc
        self.stdout = sys.stdout
    def __enter__(self):
        if sys.stdout.encoding is None:
            w = codecs.getwriter(self.enc)
            sys.stdout = w(sys.stdout)
    def __exit__(self, exc_ty, exc_val, tb):
        sys.stdout = self.stdout

用法:

with EncodedOut('utf-8'):
    print u'ÅÄÖåäö'

答案 6 :(得分:2)

我可以"自动化"它呼吁:

def __fix_io_encoding(last_resort_default='UTF-8'):
  import sys
  if [x for x in (sys.stdin,sys.stdout,sys.stderr) if x.encoding is None] :
      import os
      defEnc = None
      if defEnc is None :
        try:
          import locale
          defEnc = locale.getpreferredencoding()
        except: pass
      if defEnc is None :
        try: defEnc = sys.getfilesystemencoding()
        except: pass
      if defEnc is None :
        try: defEnc = sys.stdin.encoding
        except: pass
      if defEnc is None :
        defEnc = last_resort_default
      os.environ['PYTHONIOENCODING'] = os.environ.get("PYTHONIOENCODING",defEnc)
      os.execvpe(sys.argv[0],sys.argv,os.environ)
__fix_io_encoding() ; del __fix_io_encoding

是的,如果这个" setenv"可能会在这里获得无限循环。失败。

答案 7 :(得分:1)

在Ubuntu 12.10和GNOME终端上,当程序打印到stdout或挂钩到其他程序的管道时,不会产生错误。文件编码和终端编码都是UTF-8

$ cat a.py
# -*- coding: utf-8 -*-
print "åäö"
$ python a.py
åäö
$ python a.py | tee out
åäö

您使用的操作系统和终端模拟器是什么?我听说我的一些同事在使用iTerm 2和OS X时遇到了类似的问题; iTerm 2可能是罪魁祸首。

更新:此答案有误 - 请参阅评论以了解详情

答案 8 :(得分:1)

我只是觉得我在这里提到了一些我不得不花费很长时间进行实验的东西,然后我终于意识到发生了什么。对于这里的每个人来说,这可能是如此明显,以至于他们并没有提到它。但如果有的话,它会帮助我,所以基于这个原则......!

注意:我特别使用Jython,v 2.7,所以这可能不适用于CPython ......

NB2:我的.py文件的前两行是:

# -*- coding: utf-8 -*-
from __future__ import print_function

“%”(AKA“插值运算符”)字符串构造机制也会导致ADDITIONAL问题...如果“环境”的默认编码是ASCII并且您尝试执行类似

的操作
print( "bonjour, %s" % "fréd" )  # Call this "print A"

在Eclipse中运行没有任何困难...在Windows CLI(DOS窗口)中,您会发现编码是code page 850(我的Windows 7操作系统)或类似的东西,它可以处理欧洲重音字符至少,所以它会起作用。

print( u"bonjour, %s" % "fréd" ) # Call this "print B"

也可以。

如果,OTOH,你直接从CLI导入文件,stdout编码将是None,默认为ASCII(无论如何我的操作系统),它将无法处理上述任何一个打印... (可怕的编码错误)。

那么你可能会想到使用

重定向你的标准输出
sys.stdout = codecs.getwriter('utf8')(sys.stdout)

并尝试在CLI管道中运行到文件...非常奇怪,上面的打印A将起作用...但上面的打印B将抛出编码错误!但是,以下内容可以正常工作:

print( u"bonjour, " + "fréd" ) # Call this "print C"

我(临时)得出的结论是,如果使用“u”前缀指定为Unicode字符串的字符串被提交给%处理机制,则似乎涉及使用默认环境编码,无论您是否将stdout设置为重定向!

人们如何处理这个问题是一个选择问题。我欢迎一位Unicode专家说出为什么会发生这种情况,我是否在某种程度上弄错了,这是什么首选解决方案,是否也适用于CPython,是否发生在Python 3中等等。等等。

答案 9 :(得分:1)

我在遗留应用程序中遇到了这个问题,很难确定打印的位置。我帮助自己解决了这个问题:

# encoding_utf8.py
import codecs
import builtins


def print_utf8(text, **kwargs):
    print(str(text).encode('utf-8'), **kwargs)


def print_utf8(fn):
    def print_fn(*args, **kwargs):
        return fn(str(*args).encode('utf-8'), **kwargs)
    return print_fn


builtins.print = print_utf8(print)

在我的脚本之上,test.py:

import encoding_utf8
string = 'Axwell Λ Ingrosso'
print(string)

请注意,这会更改所有要打印的调用以使用编码,因此您的控制台将打印此内容:

$ python test.py
b'Axwell \xce\x9b Ingrosso'

答案 10 :(得分:0)

在Windows上,从编辑器(如Sublime Text)运行Python代码时,我经常遇到此问题,但从命令行运行它时,不是

在这种情况下,请检查编辑器的参数。对于SublimeText,此Python.sublime-build解决了该问题:

{
  "cmd": ["python", "-u", "$file"],
  "file_regex": "^[ ]*File \"(...*?)\", line ([0-9]*)",
  "selector": "source.python",
  "encoding": "utf8",
  "env": {"PYTHONIOENCODING": "utf-8", "LANG": "en_US.UTF-8"}
}