在Python中管道标准输出时设置正确的编码

问题描述 投票:326回答:10

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

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

正常运行时可以正常工作,但失败:

UnicodeEncodeError:'ascii'编解码器无法在位置0编码字符u'\ xa0':序数不在范围(128)中

当在管道序列中使用时。

配管时使这项工作最佳的方法是什么?我能告诉它使用外壳程序/文件系统/正在使用的任何编码吗?

到目前为止,我所看到的建议是直接修改您的site.py,或使用此技巧对defaultencoding进行硬编码:

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

是否有更好的方法使管道工作?

python encoding terminal stdout python-2.x
10个回答
157
投票

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

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

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

另一个教学示例是在ISO-8859-1和UTF-8之间进行转换的Python程序,使两者之间都大写。

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的事实。不要这样做。


0
投票

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


167
投票

首先,关于此解决方案:

# -*- 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)

125
投票

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

博客文章的Tl; dr:

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
ö ☺ ☻

61
投票
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)

更新以回复评论:这个问题只是在管道输出到stdout时存在。我在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

5
投票

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

这是我的解决方法:

[从PyCharm菜单栏开始:文件->设置...->编辑器->文件编码,然后将:“ IDE编码”,“项目编码”和“属性文件的默认编码”全部设置为UTF-8,现在就像魅力一样。

希望这会有所帮助!


4
投票

[克雷格·麦昆的答案有争议的消毒版本。

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'ÅÄÖåäö'

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”失败,则可能在此处获得无限循环。


1
投票

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

NB:我具体使用的是Jython v 2.7,所以可能这可能不适用于CPython ...

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

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

“”%“(也称为”插值运算符“)字符串构造机制也引起了其他问题。如果”环境“的默认编码为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码(无论如何在我的OS上),它将无法处理以上任何打印...(可怕的编码错误。

因此您可能会考虑通过使用重定向标准输出

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中发生等等,等等。 。


1
投票

我在旧版应用程序中遇到了这个问题,很难确定要打印的内容。我帮了我这个忙:

© www.soinside.com 2019 - 2024. All rights reserved.