如何在Python3中使用print()函数输出字符串而不出现异常?

4
表面上看很简单的问题:如何在Python3中使用print()打印字符串?应该很简单:
print(my_string)

但那样做是不行的。根据my_string的内容、使用的操作系统和环境变量,这会抛出一个UnicodeEncodeError异常:

>>> print("\u3423")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode character '\u3423' in position 0: ordinal not in range(128)

有没有一种干净便携的方法来解决这个问题?
稍微扩展一下:这里的问题是Python3字符串包含Unicode编码字符,而终端可以具有任何编码。如果你很幸运,你的终端可以处理字符串中包含的所有字符,一切都会很好,如果你的终端无法处理(例如,有人设置了LANG=C),那么你会得到一个异常。
如果你在Python3中手动编码一个字符串,你可以提供一个错误处理程序来忽略或替换不能编码的字符:
  "\u3423".encode("ascii", errors="replace")

对于print(),我没有看到一种简单的方法来插入错误处理程序,即使有,一个普通的错误处理程序似乎是一个可怕的想法,因为它会修改数据。一个条件错误处理程序可能有效(即检查isatty()并根据此决定要做什么),但这似乎是一种非常不专业的方法,只是为了打印一个字符串,而且我甚至不确定在某些情况下它是否会失败。
这个问题的一个真实例子是: Python3: UnicodeEncodeError only when run from crontab

1
在我的Linux上运行良好。您可能需要指定哪些操作系统和环境变量给您带来了麻烦。 - John1024
在我的Mac OS X上运行良好。打印出一个(中文?)字符? - Nitish
可以在我的Linux控制台和X终端仿真器上使用,但两者都配置了正确的UTF-8支持。我想知道,在您接收到此错误的环境中,UTF-8是否总体上有效?也许这不仅仅是Python的问题。 - Two-Bit Alchemist
1
如果stdout是tty,则可以替换它:sys.stdout = io.TextIOWrapper(sys.stdout.detach(), errors='backslashreplace') - Eryk Sun
1
使用 LANG=C python3 -c 'print("\u3423")',我可以重现你的错误,而使用 LANG=en_US.UTF-8 就能正常工作。 - John1024
显示剩余2条评论
3个回答

2

解决这个问题最实用的方法似乎是强制使用 utf-8:surrogateescape 输出编码。这不仅会强制输出UTF-8,还可以确保由 os.fsdecode() 返回的代理转义字符串可以被打印而不会抛出异常。在命令行中,它看起来像这样:

PYTHONIOENCODING=utf-8:surrogateescape python3 -c 'print("\udcff")'

要在程序内部完成此操作,需要重新分配 stdoutstderr。可以使用以下方法实现(line_buffering=True 很重要,否则输出将无法正确刷新):
import sys
import io

sys.stdout = io.TextIOWrapper(sys.stdout.buffer, errors="surrogateescape", line_buffering=True)
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, errors="surrogateescape", line_buffering=True)

print("\udcff")

这种方法会导致未设置为UTF-8的终端显示字符错误,但我认为这比随机抛出异常并使打印文件名变得不可能更好,因为它们在Linux系统上可能根本不是任何有效编码。我在一些地方读到,utf-8:surrogateescape 有可能会成为默认值,但是在Python 3.6.0b2中,这并不是默认情况。

嘿,谢谢!这正是我一直在寻找的 :) - Laurent'

1

有没有一种干净便携的方法来解决这个问题?

设置 PYTHONIOENCODING=<encoding>:<error_handler>,例如:

$ PYTHONIOENCODING=utf-8 python your_script.py >output-in-utf-8.txt

在您的情况下,我会配置您的环境(LANGLC_CTYPE)以接受非ASCII输入:
$ locale charmap

-2

它给你报错的原因是因为它试图解释什么是\u。就像\r是回车符的ASCII码,\n代表换行符,\t代表制表符等等...

如果:

 my_string = '\u112'
 print(my_string)

这会导致错误,如果要打印 '\' 而不是让它尝试找出 '\' 是什么,可以这样写:

 my_string = '\\u122'
 print(my_string)

输出:

 \u122

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