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

当输送Python程序的输出时,Python解释器会混淆编码并将其设置为None。这意味着这样一个程序:

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

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

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

在管道序列中使用时。

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

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

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

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

253800 次浏览

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

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

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

每次都显式地用给定的编码打印是不实际的。这将是重复的,而且容易出错。

更好的解决方案是在程序开始时更改sys.stdout,以使用选定的编码进行编码。下面是我在如何选择sys.stdout.encoding ?上找到的一个解决方案,特别是“toka”的注释:

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

您可能想尝试将环境变量“PYTHONIOENCODING”更改为“utf_8”。我已经写了一个我在这个问题上的痛苦经历

博客文章的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
ö ☺ ☻
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

猫b.py

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


print sys.stdout.encoding

运行。/ b.py

UTF-8

运行。/b.p y| less

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”失败了,就有可能得到一个无限循环。

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

注:我具体使用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窗口)中,你会发现编码是代码页850(我的Windows 7操作系统)或类似的东西,至少可以处理欧洲口音的字符,所以它可以工作。

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

也会起作用。

如果,OTOH,你从CLI直接到一个文件,标准输出编码将是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的,它是否发生在python3中,等等,等等。

克雷格·麦昆(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菜单栏开始:File -> Settings…->编辑器->文件编码,然后设置:“IDE编码”,“项目编码”和“属性文件的默认编码”全部为UTF-8,她现在的工作就像一个魅力。

希望这能有所帮助!

我在一个遗留应用程序中遇到了这个问题,并且很难确定在哪里打印了内容。我用这个方法帮助自己:

# 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'

在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"}
}

从Python 3.7开始,我们可以使用Python UTF-8模式,通过使用命令行选项-X utf8:

 python -X utf8 testzh.py

脚本testzh.py包含

print("Content-type: text/html; charset=UTF-8\n")
print("地球你好!")


将Windows 10 Internet Service IIS设置为CGI脚本处理程序

我们将Executable设置为:

"C:\Program Files\Python39\python.exe" -X utf8 %s

enter image description here

这适用于微软浏览器上的中文表意文字。像这样的截图:否则,错误发生。

enter image description here

请参见https://docs.python.org/3/library/os.html#utf8-mode

我很惊讶这个答案还没有被贴在这里

从Python 3.7开始,你可以用reconfigure()改变标准流的编码:

sys.stdout.reconfigure(encoding='utf-8')

你也可以通过添加errors参数来修改编码错误的处理方式。

https://stackoverflow.com/a/52372390/15675011 < a href = " https://stackoverflow.com/a/52372390/15675011 " > < / >