【问题标题】:How to write utf8 to standard output in a way that works with python2 and python3如何以适用于 python2 和 python3 的方式将 utf8 写入标准输出
【发布时间】:2014-05-30 00:00:18
【问题描述】:

我想写一个非 ascii 字符,比如说 到标准输出。棘手的部分似乎是我想连接到该字符串的一些数据是从 json 读取的。考虑以下简单的 json 文档:

{"foo":"bar"}

我包含这个是因为如果我只想打印,那么简单地写似乎就足够了:

print("→")

它会在 python2 和 python3 中做正确的事情。

所以我想打印foo 的值和我的非ascii 字符。我发现这样做使其在 python2 和 python3 中都有效的唯一方法是:

getattr(sys.stdout, 'buffer', sys.stdout).write(data["foo"].encode("utf8")+u"→".encode("utf8"))

getattr(sys.stdout, 'buffer', sys.stdout).write((data["foo"]+u"→").encode("utf8"))

重要的是不要错过前面的u,否则python2会抛出UnicodeDecodeError

像这样使用print函数:

print((data["foo"]+u"→").encode("utf8"), file=(getattr(sys.stdout, 'buffer', sys.stdout)))

似乎不起作用,因为 python3 会抱怨 TypeError: 'str' does not support the buffer interface

我找到了最好的方法还是有更好的选择?我可以让打印功能工作吗?

【问题讨论】:

  • 所以print(data['foo'] + u'→') 不起作用?
  • @user2357112:不在我的机器上。
  • 对于调用 print 的最后一个示例,在 Python 3 编码中,字符串返回 bytes。由于print 需要一个字符串,它调用__str__ 方法,对于bytes 只返回一个repr,即str("→".encode()) == "b'\\xe2\\x86\\x92'"。接下来print将这个无用的repr写入file,但是BufferedWriter需要一个支持buffer接口的对象,比如bytes
  • @eryksun 谢谢!由于print() 能够打印各种数据类型而无需显式转换为str 我不认为它会阻塞bytes
  • 打印必须先获取一个对象作为字符串。这不会让 Python 3 bytes 窒息。使用默认编码解码bytes 通常是错误的,因为bytes 对象不一定是文本。我只是说 repr 字符串对于您的需求是“无用的”。令人窒息的是试图打印到BufferedWriter,例如print('abc', file=sys.stdout.buffer).

标签: python python-3.x encoding stdout


【解决方案1】:

我能想到的最简洁的是以下内容,您可以通过一些便利功能(甚至替换/覆盖打印功能)使其更简洁:

# -*- coding=utf-8 -*-
import codecs
import os
import sys

# if you include the -*- coding line, you can use this
output = 'bar' + u'→'
# otherwise, use this
output = 'bar' + b'\xe2\x86\x92'.decode('utf-8')

if sys.stdout.encoding == 'UTF-8':
    print(output)
else:
    output += os.linesep
    if sys.version_info[0] >= 3:
        sys.stdout.buffer.write(bytes(output.encode('utf-8')))
    else:
        codecs.getwriter('utf-8')(sys.stdout).write(output)

最好的选择是使用 -*- 编码行,它允许您使用文件中的实际字符。但是如果由于某种原因,你不能使用编码线,它仍然可以完成。

这(有和没有编码行)适用于 Linux (Arch) 和 python 2.7.7 和 3.4.1。 如果终端的编码不是 UTF-8,它也可以工作。 (在 Arch Linux 上,我只是通过使用不同的 LANG 环境变量来更改编码。)

LANG=zh_CN python test.py

它也在某种程度上适用于 Windows,我在 2.6、2.7、3.3 和 3.4 上尝试过。 有点,我的意思是我可以让'→' 字符只显示在薄荷终端上。在 cmd 终端上,该字符将显示为 'ΓåÆ'。 (那里可能缺少一些简单的东西。)

【讨论】:

  • 对于 Windows 仅能正常工作,将 'utf-8' 更改为 sys.stdout.encoding 打印会更好吗?
  • 没有。这与简单地进行打印相同。如果您不更改编码,sys.stdout.encoding 就是它使用的编码,这就是为什么要进行所有工作来更改它的默认值。
  • 作为一个实验,试试代码here。它将显示终端上使用的所有可用编码的编码效果——对于那些不抛出异常的编码。我在 Windows 和 Linux、2.7 和 3.4 上运行它。
  • 我不能再强调确保您的终端或控制台正确配置的重要性。确保这一点不应该是 Python 的工作。就个人而言,我会使用output = output.encode('utf-8')try:sys.stdout.buffer.write(output)except AttributeError:sys.stdout.write(output)codecs.getwriter() 在这里太过分了,您需要测试功能,而不是版本。您也可以在 Python 2 中使用 io 模块,因此 sys.stdout 实际上也可以在那里拥有 .buffer 属性。
  • @MartijnPieters 是否有关于如何在 Windows 上正确配置控制台/终端(cmd/powershell/其他?)的教程或参考?
【解决方案2】:

如果您不需要打印到sys.stdout.buffer,那么下面的内容应该可以打印到sys.stdout。我在 Python 2.7 和 3.4 中都试过了,它似乎工作正常:

# -*- coding=utf-8 -*-
print("bar" + u"→")

【讨论】:

  • 这在sys.stdout.encoding != "UTF-8" 下不起作用,例如在 Windows 上。
  • @snapshoe 很明显,如果输出到某些功能有限的东西,它将无法正确显示。但是 Python 确实以 UTF-8 写入输出,并且 OP 似乎希望将输出发送到一个文件中。
  • @rds 我没有看到任何提到输出到文件的内容。我确实看到到处都提到,包括帖子的标题,关于打印到标准输出。
猜你喜欢
  • 2014-07-18
  • 2018-09-09
  • 2019-06-19
  • 2014-07-03
  • 2020-09-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多