如何在Python < 3中将UTF-8编码的文本打印到控制台?

56

我正在运行一个最新的Linux系统,其中所有的本地化设置都是UTF-8:

LANG=de_DE.UTF-8
LANGUAGE=
LC_CTYPE="de_DE.UTF-8"
LC_NUMERIC="de_DE.UTF-8"
LC_TIME="de_DE.UTF-8"
...
LC_IDENTIFICATION="de_DE.UTF-8"
LC_ALL=

现在我想将UTF-8编码的内容写入控制台。

目前,Python使用UTF-8作为文件系统编码,但默认编码仍然是ASCII:-(

>>> import sys
>>> sys.getdefaultencoding()
'ascii'
>>> sys.getfilesystemencoding()
'UTF-8'

我原以为最好(干净)的方法是设置PYTHONIOENCODING环境变量。但似乎Python会忽略它。至少在我的系统上,即使设置了环境变量,我仍然会得到默认编码ascii。
# tried this in ~/.bashrc and ~/.profile (also sourced them)
# and on the commandline before running python
export PYTHONIOENCODING=UTF-8

如果我在脚本开头执行以下操作,它就可以正常工作:
>>> import sys
>>> reload(sys)  # to enable `setdefaultencoding` again
<module 'sys' (built-in)>
>>> sys.setdefaultencoding("UTF-8")
>>> sys.getdefaultencoding()
'UTF-8'

但是这种方法看起来不太干净。那么,有什么好的方法可以实现这个目标呢?

解决方法

与其更改默认编码(这不是一个好主意,请参见mesilliac的答案),我只需像这样包装sys.stdoutStreamWriter

sys.stdout = codecs.getwriter(locale.getpreferredencoding())(sys.stdout)

请查看此代码片段,其中包含一个小型实用函数,可处理此问题。


1
也许这个可以工作:#!/usr/bin/env python # -- coding: utf-8 -- - chessweb
8
这只影响 Python 如何解释源代码中的字面字符串。IO 编码仍将为 ASCII。 - Keith
8
PYTHONIOENCODING并没有被忽略;正如其名称所示,它会影响标准输入/输出/错误的编码(参见https://docs.python.org/2/using/cmdline.html#environment-variables),而这不是您使用[`sys.getdefaultencoding()`](https://docs.python.org/2/library/sys.html#sys.getdefaultencoding)检查的内容。 - musiphil
3
@Brutus:你是怎么测试它不起作用的?在我这里似乎是可以工作的。 python -c 'import sys; print sys.stdout.encoding' 输出 UTF-8,而 PYTHONIOENCODING='C' python -c 'import sys; print sys.stdout.encoding' 输出 C - musiphil
您的区域设置用于确定要应用哪种编码 sys.stdout.encoding。安装不正确的区域设置可能会导致将 sys.stdout.encoding 设置为 ASCII$ locale 应该无错误返回。 - Alastair McCormack
显示剩余3条评论
5个回答

29

看起来不建议这样做。

Fedora建议使用系统区域设置作为默认值,但是显然会破坏其他东西。

这是来自邮件列表讨论的一句话:

Python 中唯一支持的默认编码是:
Python 2.x: ASCII Python 3.x: UTF-8
如果更改这些设置,你将独自面对奇怪的问题。默认编码不仅影响 Python 和外界之间的转换,还会影响所有 8 位字符串和 Unicode 之间的内部转换。
像在 pango 模块中发生的那种 hack(通过重新加载 site 模块以获得 sys.setdefaultencoding() API,并将默认编码设置为 'utf-8')是完全错误的,会引起严重问题,因为 Unicode 对象缓存其默认编码表示形式。
请勿启用基于区域设置的默认编码。
如果你想要实现的只是正确设置管道的 stdout 和 stdin 的编码,请改变它们的 .encoding 属性(仅限它们)。
-- Marc-Andre Lemburg eGenix.com

24

这是我的做法:

#!/usr/bin/python2.7 -S

import sys
sys.setdefaultencoding("utf-8")
import site

注意bangline中的-S。这告诉Python不要自动导入site模块。 site模块是设置默认编码和删除该方法的模块,因此无法再次设置。但它将尊重已经设置的内容。


您能否根据mesilliac的回答进一步阐述一下?这个回答仍然正确吗? - Arafangion
1
@Arafangion 我使用的方法发生在Python初始化的最开始。此时还没有创建任何缓存。我同意使用reload技巧是不好的。这是因为许多其他东西可能已经被实例化或缓存了原始编码。因此,我想出了这种早期的方法。请注意,在它之前没有其他导入。这对我很有效。 - Keith
6
这是一个非常糟糕的想法。在过去几周中,我解决了两个问题,这些问题是通过从用户代码中删除sys.setdefaultencoding("utf-8")来解决的。我认为,这只是掩盖了任何潜在的问题。 - Alastair McCormack
@AlastairMcCormack 我已经使用过这个,没有遇到任何问题。只要你知道发生了什么,就没有问题。你认为它掩盖了哪些潜在问题? - Keith
这个方法非常有效,但我不建议在复杂的程序中使用。如果您正在使用自定义代码并依赖于非ASCII内容,请重新考虑使用默认为UTF8的语言。 - buckaroo1177125
显示剩余9条评论

10
如何在Python < 3中将UTF-8编码的文本输出到控制台?
print u"some unicode text \N{EURO SIGN}"
print b"some utf-8 encoded bytestring \xe2\x82\xac".decode('utf-8')

即,如果您有一个Unicode字符串,则直接打印它。如果您有一个字节字符串,则先将其转换为Unicode。

您的区域设置(LANGLC_CTYPE)指示了一个utf-8区域设置,因此(理论上)您可以直接打印一个utf-8字节字符串,并且它应该在终端中正确显示(如果终端设置与区域设置一致并且它们应该是),但您应该避免这样做:不要在脚本内部硬编码您的环境的字符编码直接打印Unicode

您的问题中有许多错误的假设。

您不需要使用区域设置设置PYTHONIOENCODING来将Unicode打印到终端。 utf-8区域设置支持所有Unicode字符,即,它可以正常工作。

您不需要使用这个解决方法sys.stdout = codecs.getwriter(locale.getpreferredencoding())(sys.stdout)。它可能会在某些情况下(您无法控制的代码)需要打印字节时出现错误,也可能会在将Unicode打印到Windows控制台时出现问题(错误的代码页,无法打印无法解码的字符)时出现错误。正确的区域设置和/或PYTHONIOENCODING环境变量就足够了。此外,如果您需要替换sys.stdout,那么使用io.TextIOWrapper()代替codecs模块,就像win-unicode-console一样。

sys.getdefaultencoding()与您的区域设置和PYTHONIOENCODING无关。 您认为设置PYTHONIOENCODING应该更改sys.getdefaultencoding()的假设是不正确的。 您应该检查sys.stdout.encoding

在打印到控制台时,不使用sys.getdefaultencoding()。 在Python 2中,如果stdout被重定向到文件/管道,则可能被用作后备,除非设置了PYTHOHIOENCODING

$ python2 -c'import sys; print(sys.stdout.encoding)'
UTF-8
$ python2 -c'import sys; print(sys.stdout.encoding)' | cat
None
$ PYTHONIOENCODING=utf8 python2 -c'import sys; print(sys.stdout.encoding)' | cat
utf8

请勿调用 sys.setdefaultencoding("UTF-8");这可能会导致您的数据被 悄无声息地 损坏和/或破坏第三方模块,因为它们不期望此操作。请记住,在 Python 2 中,sys.getdefaultencoding() 隐式地用于将字节串(str)转换为 / 从 unicode,例如 "a" + u"b"。另请参见 @mesilliac答案中引用的话

6
如果程序在屏幕上没有显示出适当的字符,即无效符号,请使用以下命令行运行程序:
PYTHONIOENCODING=utf8 python3 yourprogram.py

如果你的程序是全局安装的模块,那么可以使用以下方法:
PYTHONIOENCODING=utf8 yourprogram

在某些平台上,例如带有 Anaconda 的 Cygwin(mintty.exe 终端)中使用 Python(或 Python 3),只需运行 export PYTHONIOENCODING=utf8,然后运行程序不能正常工作,需要每次都运行 PYTHONIOENCODING=utf8 yourprogram 才能正确运行程序。

在 Linux 上,在使用 sudo 的情况下,可以尝试传递 -E 参数将用户变量导出到 sudo 进程:

export PYTHONIOENCODING=utf8
sudo -E python yourprogram.py

如果您尝试了这个方法,但并没有生效,您需要进入sudo shell:

sudo /bin/bash
PYTHONIOENCODING=utf8 yourprogram

相关文章:

  1. 如何在Python < 3中将UTF-8编码的文本打印到控制台?
  2. 如何更改Python的默认编码?
  3. 如何在Python3中强制使用UTF-8而非cp1252?
  4. 如何在Cygwin中永久设置Anaconda的Python路径?
  5. https://superuser.com/questions/1374339/what-does-the-e-in-sudo-e-do
  6. 为什么bash -c 'var=5 printf "$var"'不会打印5?
  7. https://unix.stackexchange.com/questions/296838/whats-the-difference-between-eval-and-exec

utf8 是区分大小写的吗?此外,utf8 是唯一可能的设置吗?还是 utf-8 也是有效的?我看到了很多变体,所以想确认一下...(你在回答中使用了其中两个!) - Gwyneth Llewelyn
1
我认为至少对于我的Python 3.7.2,使用UTF-8是不区分大小写的,而且我不确定它是否忽略了UTF-8中的连字符。 - user
这很有道理 - 我正在使用Python 2.7.X,并且不确定该使用什么... - Gwyneth Llewelyn

3

尽管意识到 OP 的问题是关于 Linux 的,但当通过搜索引擎进入此处时,在 Windows 10 上,以下方法可以解决此问题:

set PYTHONIOENCODING=utf8
python myscript.py

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接