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

在Python中管道化stdout时设置正确的编码,python,encoding,terminal,stdout,python-2.x,Python,Encoding,Terminal,Stdout,Python 2.x,当管道传输Python程序的输出时,Python解释器会对编码感到困惑,并将其设置为“无”。这意味着像这样的程序: # -*- coding: utf-8 -*- print u"åäö" 正常运行时工作正常,但出现以下故障: UnicodeEncodeError:“ascii”编解码器无法对位置0中的字符u'\xa0'进行编码:序号不在范围内(128) 在管道序列中使用时 配管时,最好的方法是什么?我能告诉它使用shell/文件系统/正在使用的任何编码吗 到目前为止,我看到的建议是直接修改s

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

# -*- coding: utf-8 -*-
print u"åäö"
正常运行时工作正常,但出现以下故障:

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

在管道序列中使用时

配管时,最好的方法是什么?我能告诉它使用shell/文件系统/正在使用的任何编码吗

到目前为止,我看到的建议是直接修改site.py,或者使用以下方法对defaultencoding进行硬编码:

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

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

在脚本中运行时,代码可以工作,因为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的事实。不要这样做。

首先,关于此解决方案:

# -*- coding: utf-8 -*-
print u"åäö".encode('utf-8')
每次都使用给定的编码显式打印是不实际的。这将是重复和容易出错的

更好的解决方案是在程序开始时将sys.stdout更改为使用选定的编码。以下是我找到的一个解决方案,特别是“toka”的评论:


您可能希望尝试将环境变量“pythonionecoding”更改为“utf_8”。我写了一封信

Tl;博文博士:

导入系统、区域设置、操作系统
打印(系统标准编码)
打印(sys.stdout.isatty())
打印(locale.getpreferredencoding())
打印(sys.getfilesystemencoding())
打印(os.Enviro[“PYTHONIOENCODING”])
印刷品(chr(246)、chr(9786)、chr(9787))
给你

utf_8
假的
ANSI_X3.4-1968
ascii码
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)
更新以答复评论: 当管道连接到标准管道时,问题就存在了。 我在Fedora25Python 2.7.13中进行了测试

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
我可以通过呼叫“自动化”它:

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”失败的话,这里可能会有一个无限循环。

我只是想在这里提到一些东西,在我最终意识到发生了什么之前,我花了很长时间进行了实验。这对在座的每个人来说都是显而易见的,以至于他们都懒得提及它。但如果他们有,这会对我有帮助,所以根据这个原则

注意:我使用的是V2.7,所以这可能不适用于

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

# -*- coding: utf-8 -*-
from __future__ import print_function
“%”(也称为“插值运算符”)字符串构造机制也会导致其他问题。。。如果“环境”的默认编码是ASCII,并且您尝试执行以下操作

print( "bonjour, %s" % "fréd" )  # Call this "print A"
在Eclipse中运行不会有任何困难。。。在Windows CLI(DOS窗口)中,您会发现编码是(my Windows7 OS)或类似的编码,它至少可以处理欧洲口音字符,因此它可以工作

print( u"bonjour, %s" % "fréd" ) # Call this "print B"
也会起作用

如果,oth,您从CLI指向一个文件,则标准输出编码将为None,默认为ASCII(无论如何在我的操作系统上),这将无法处理上述任何一种打印。。。(可怕的编码错误)

因此,您可能会考虑使用

sys.stdout = codecs.getwriter('utf8')(sys.stdout)
并尝试在CLI管道中运行到文件。。。非常奇怪的是,上面的打印A会起作用。。。但是上面的打印B将抛出编码错误!但是,以下各项可以正常工作:

print( u"bonjour, " + "fréd" ) # Call this "print C"
我(暂时)得出的结论是,如果一个使用“u”前缀指定为字符串的字符串被提交到%-处理机制,那么它似乎涉及使用默认环境编码,,而不管您是否已将stdout设置为重定向


人们如何处理这一问题是一个选择的问题。我欢迎一位Unicode专家解释为什么会发生这种情况,我是否在某些方面弄错了,首选的解决方案是什么,它是否也适用于,它是否发生在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'ÅÄÖåäö'
我吃了一顿饭。在我的IDE(PyCharm)中很容易修复

这是我的解决方案:

从PyCharm菜单栏开始:文件->设置…->编辑器->文件编码,然后将“IDE编码”、“项目编码”和“属性文件的默认编码”全部设置为UTF-8,她现在可以像个魔咒一样工作了


希望这有帮助

我在一个遗留应用程序中遇到了这个问题,很难确定打印内容的位置。我自己动手做了这件事:

# -*- coding: utf-8 -*-
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
print u"åäö"
#编码_utf8.py
导入编解码器
进口内置设备
def print_utf8(文本,**kwargs):
打印(str(文本)。编码('utf-8'),**kwargs)
def打印_utf8(fn):
def打印格式(*args,**kwargs):
返回fn(str(*args).encode('utf-8'),**kwargs)
返回打印\u fn
builtins.print=print\u utf8(打印)
在我的脚本test.py上:

导入编码\u utf8
字符串='Axwell∧Ingrosso'
打印(字符串)
请注意,这会将所有打印调用更改为使用编码,因此您的控制台将打印以下内容:

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

在Windows上,当从编辑器(如Sublime Text)运行Python代码时,我经常遇到这个问题,但如果从命令行运行它,就不会出现这个问题

在这种情况下,请检查编辑器的参数。对于SublimateText,这个
Python.sublimate 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"}
}

不幸的是,将sys.stdout更改为只接受unicode会破坏许多库
{
  "cmd": ["python", "-u", "$file"],
  "file_regex": "^[ ]*File \"(...*?)\", line ([0-9]*)",
  "selector": "source.python",
  "encoding": "utf8",
  "env": {"PYTHONIOENCODING": "utf-8", "LANG": "en_US.UTF-8"}
}