在Python中pipe理标准输出时设置正确的编码

当对Python程序的输出进行pipe道输送时,Python解释器会对编码感到困惑,并将其设置为None。 这意味着这样一个程序:

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

在正常运行时会正常工作,但会失败:

UnicodeEncodeError:'ascii'编解码器不能在位置0编码字符u'\ xa0':序号不在范围内(128)

当在pipe道序列中使用时。

当pipe道工作时,最好的方法是什么? 我可以告诉它使用任何编码的shell /文件系统/任何正在使用?

我迄今为止所看到的build议是直接修改你的site.py,或者使用这个黑客来硬编码默认编码:

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

有没有更好的方法来使pipe道工作?

您的代码在脚本中运行时工作,因为Python将输出编码为terminal应用程序正在使用的任何编码。 如果你是pipe道系统,你必须自己编码。

经验法则是:始终在内部使用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的事实。 不要这样做。

首先,关于这个解决scheme:

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

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

更好的解决scheme是在程序开始时更改sys.stdout ,使用选定的编码进行编码。 这里是我在Python上find的一个解决scheme:sys.stdout.encoding是如何select的? 特别是“toka”的评论:

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

您可能需要尝试将环境variables“PYTHONIOENCODING”更改为“utf_8”。 我已经写了一个关于这个问题的考验 。

Tl;博客文章的博士:

 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 ö ☺ ☻ 
 export PYTHONIOENCODING=utf-8 

做这项工作,但不能在Python本身设置…

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

 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) 

更新回复评论:pipe道到标准输出时,问题就存在。 我在Fedora 25 Python 2.7.13中testing过

 python --version Python 2.7.13 

猫b.py

 #!/usr/bin/env python #-*- coding: utf-8 -*- import sys print sys.stdout.encoding 

运行./b.py

 UTF-8 

运行./b.py | 减

 None 

上周我有类似的问题 。 在我的IDE(PyCharm)中很容易修复。

这是我的修复:

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

希望这可以帮助!

我可以通过以下方式“自动化”:

 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”失败,可以在这里得到一个无限循环。

我只是想我会在这里提到一些事情,在我终于意识到发生了什么事之前,我不得不花费很长时间进行试验。 对于这里的每个人来说,这可能是非常明显的,他们没有提到这一点。 但是,如果他们有,那么它会帮助我,所以这个原则…!

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

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

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

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

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

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

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

也将工作。

如果OTOH从CLI直接到文件,标准输出编码将是None,它将默认为ASCII(在我的操作系统上),它将无法处理上述打印中的任何一个…(可怕的编码错误)。

那么你可能会想到通过使用redirect你的stdout

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

并尝试在CLIpipe道中运行到一个文件…非常奇怪的是,打印A上面将工作…但上面的打印B会抛出编码错误! 以下将工作正常:

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

我临时得出的结论是,如果一个被指定为使用“u”前缀的Unicodestring的string被提交给%处理机制,它似乎涉及使用默认的环境编码, 而不pipe是否已经设置stdoutredirect!

人们如何处理这个问题是一个select问题。 我希望Unicode专家能够说出为什么会发生这样的情况,不pipe我在某种程度上是否有错误,首选的解决scheme是什么,它是否也适用于CPython ,是否发生在Python 3等等。

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

在Ubuntu 12.10和GNOME Terminal上,当程序打印到标准输出或挂钩到其他程序的pipe道时,不会产生错误。 文件编码和terminal编码都是UTF-8 。

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

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

更新:这个答案是错误的 – 请参阅评论的细节