ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

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

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

在管道序列中使用时。

管道时完成这项工作的最佳方法是什么?我可以告诉它使用外壳/文件系统/正在使用的任何编码吗?

到目前为止,我看到的建议是直接修改您的 site.py,或使用此 hack 硬编码默认编码:

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

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

如果您在 Windows 上遇到此问题,您也可以在执行脚本之前运行 chcp 65001。这可能会出现问题,但通常会有所帮助,并且不需要大量输入(少于 set PYTHONIOENCODING=utf_8)。
chcp 命令与设置 PYTHONIOENCODING 不同。我认为 chcp 只是终端本身的配置,与写入文件无关(这是您在管道标准输出时所做的)。如果您想节省输入,请尝试 setx PYTHONENCODING utf-8 使其永久化。
我遇到了一个相关的问题,并在这里找到了解决方案--> stackoverflow.com/questions/48782529/…

P
Peter Mortensen

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


问题是用户不想明确指定编码。他只想将 Unicode 用于 IO。而且他使用的编码应该是语言环境设置中指定的编码,而不是终端应用程序设置中指定的编码。 AFAIK,在这种情况下,Python 3 使用 locale 编码。更改 sys.stdout 似乎是一种更愉快的方式。
这个答案是错误的。您不应该手动转换程序的每个输入和输出;这是脆弱的,完全无法维护。
@Glenn Maynard:那么 IYO 的正确答案是什么?告诉我们比说“这个答案是错误的”更有帮助
@smci:答案是不要修改您的脚本,如果您在 Python 2 中重定向脚本的标准输出,请设置 PYTHONIOENCODING
@Glenn Maynard 实际上,解码和编码是一种很好的做法,来自 python doc:“软件应仅在内部使用 Unicode 字符串,尽快解码输入数据并仅在最后对输出进行编码。”
C
Craig McQueen

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

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

不幸的是,将 sys.stdout 更改为仅接受 unicode 会破坏许多希望它接受编码字节串的库。
nosklo:那么当输出是终端时,它如何可靠和自动地工作?
@Rasmus Kaj:只需定义自己的 unicode 打印函数并在每次要打印 unicode 时使用它:def myprint(unicodeobj): print unicodeobj.encode('utf-8') - 您通过检查 sys.stdout.encoding 自动检测终端编码,但您应该考虑它是 None 的情况(即,将输出重定向到文件时)所以无论如何您都需要一个单独的函数。
@nosklo:这不会使 sys.stdout 只接受Unicode。您可以将 str 和 unicode 都传递给 StreamWriter。
我假设这个答案是为 python2 准备的。在旨在同时支持 python2 和 python3 的代码上注意这一点。对我来说,在 python3 下运行时它会破坏东西。
s
sophros

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

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

更改 sys.stdout.encoding 可能不起作用,但更改 sys.stdout 确实有效:sys.stdout = codecs.getwriter(encoding)(sys.stdout)。这可以在 python 程序中完成,因此不会强制用户设置环境变量。
@jeckyll2hide:PYTHONIOENCODING 确实有效。 user 环境定义了如何将字节解释为文本。你的脚本不应该假设和规定用户环境使用什么字符编码。如果 Python 没有自动获取设置,则可以为您的脚本设置 PYTHONIOENCODING。除非将输出重定向到文件/管道,否则您不需要它。
+1。老实说,我认为这是一个 Python 错误。当我重定向输出时,我想要那些将在终端上但在文件中的相同字节。也许它不适合所有人,但它是一个很好的默认值。对通常“正常工作”的微不足道的操作没有任何解释的硬崩溃是一个糟糕的默认值。
@SnakE:我可以合理化为什么Python的实现会在启动时故意在stdout上强制执行一个铁定的和永久的编码选择的唯一方法,可能是为了防止以后出现任何编码错误的东西。或者更改它只是一个未实现的功能,在这种情况下,允许用户稍后更改它将是一个合理的 Python 功能请求。
@daveagp 我的意思是,我的程序的行为不应该取决于它是否被重定向——除非我真的想要它,在这种情况下我自己实现它。 Python 的行为与我使用任何其他控制台工具的经验相反。这违反了最小意外原则。我认为这是一个设计缺陷,除非有非常充分的理由。
S
Sérgio
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

猫b.py

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

print sys.stdout.encoding

运行./b.py

UTF-8

运行./b.py |较少的

None

该检查在 Python 2.7.13 中不起作用。 sys.stdout.encoding 是根据 LC_CTYPE 语言环境值自动设置的。
mail.python.org/pipermail/python-list/2011-June/605938.html 那里的示例仍然有效,即当您使用 ./a.py > out.txt sys.stdout.encoding 是 None
我在 Backblaze B2 的同步脚本中遇到了类似的问题,并且 export PYTHONIOENCODING=utf-8 解决了我的问题。 Debian Stretch 上的 Python 2.7。
C
CLaFarge

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

这是我的修复:

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

希望这可以帮助!


T
Tompa

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

P
Peter Mortensen

我只是想在这里提一些我必须花很长时间试验的东西,然后我才最终意识到发生了什么。这对这里的每个人来说可能都很明显,以至于他们都懒得提它。但如果他们有的话,它会帮助我,所以按照这个原则......!

注意:我使用的是 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 定向到文件,则标准输出编码将为无,默认为 ASCII(无论如何在我的操作系统上),它将无法处理上述任何一种打印...(可怕的编码错误)。

那么你可能会考虑通过使用重定向你的标准输出

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

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

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

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

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


这并不奇怪,因为 "fréd" 是一个字节序列而不是 Unicode 字符串,所以 codecs.getwriter 包装器将不理会它。您需要前导 ufrom __future__ import unicode_literals
@MatthiasUrlichs 好的......谢谢......但我只是发现编码是 IT 中最令人愤怒的方面之一。你从哪里得到你的理解?例如,我刚刚在这里发布了另一个关于编码的问题:stackoverflow.com/questions/44483067/…:这是关于 Java、Eclipse、Cygwin 和摇篮。如果您的专业知识到此为止,请帮助...最重要的是,我想知道在哪里可以了解更多信息!
j
jno

我可以通过调用来“自动化”它:

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”失败,这里可能会出现无限循环。


有趣,但管道似乎对此并不满意
c
cessor

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

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

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

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

B
Basj

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

j
jacouh

从 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 服务 IIS 设置为 CGI 脚本处理程序,

我们将 Executable 设置为:

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

https://i.stack.imgur.com/9ezXd.png

这适用于浏览器 Microsoft.Edge 上预期的中文表意文字,如下图所示:否则,会发生错误。

https://i.stack.imgur.com/dZrFs.png

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


q
qz-

我很惊讶这个答案还没有在这里发布

从 Python 3.7 开始,您可以使用 reconfigure() 更改标准流的编码: sys.stdout.reconfigure(encoding='utf-8') 您还可以通过添加错误参数来修改编码错误的处理方式。

https://stackoverflow.com/a/52372390/15675011