我使用 Python 3.9.1 和 Linux (CentOS 7)。我想将 unicode 字符打印到控制台。我想用 UTF-8 做所有事情。如果我打开python交互式控制台并编写:
print("├")
一切顺利,它打印:
├
现在,我将相同的行print("├")
放在文件中,然后使用 UTF-8 编码保存文件(Linux 上默认)。 然后我收到以下错误:
UnicodeEncodeError: 'latin-1' codec can't encode character 'u251c' in position 0: ordinal not in range(256)
"拉丁语-1"从何而来?
我还在第一行强制使用 UTF-8(无论如何这应该是 Python3 中的默认值)
# coding: utf8
但它不会改变任何东西。
有关哪些有效和哪些无效的更多信息:
s = "├"
#print(s) # FAIL
s2 = s.encode('utf8')
print(s2) # prints b'xe2x94x9c'
print(s2.decode('latin-1')) # prints the right thing
这是怎么回事?是否可以在脚本中获得与交互式控制台中相同的行为?
s = "├"
(在 UTF-8 编码的源文件中)将字符u251C
分配给 UTF-8 编码字符串s
的第一个位置。
print(s)
失败,因为此处打印绑定以将表示s
的字节发送到标准输出,该输出需要latin-1
编码。实际上,类似于s.encode('latin-1')
的东西会失败,因为字符串中的第一个字符无法正确编码。
如果你真的运行该语句(s.encode('latin-1')
),你会发现它会导致同样的错误。
s2 = s.encode('utf8')
工作正常,它告诉 Python 将s
的内容显式编码为字节序列。s2
现在使用UTF-8编码来保存s
的字节编码。(也许"b"会是一个更好的变量名,它毕竟不是一个字符串)
print(s2)
确实打印b'xe2x94x9c'
,因为它只是打印字节序列的Python表示。它不是一个字符串,因此您可以获得打印值的表示形式。它应该是,它是你可以用来定义s2
的文字,即s2 = b'xe2x94x9c'
不会改变任何事情。
print(s2.decode('latin-1'))
打印正确的东西有点神秘。s2
是 U+251C 字符 (https://www.fileformat.info/info/unicode/char/251c/index.htm) 的正确 UTF-8 字节序列
显然,您的 Python 获取s2.decode('latin-1')
的结果,再次将其编码为latin-1
字节序列,然后将其写入输出流,在那里它为您正确呈现。
由于 Python 会对尝试打印 UTF-8 编码字符串的早期打印语句执行相同的操作,因此它解释了为什么这些语句无法正确显示(或根本不显示)。
解决方案是明确告诉 Python 将标准输出的编码覆盖为 UTF-8,这样您就可以打印 UTF-8 字符串,而无需 Python 尝试将其编码为latin-1
编码字节序列(这将失败)。
如此处所述 https://docs.python.org/3/using/cmdline.html#envvar-PYTHONIOENCODING 您可以通过设置SET PYTHONENCODING=UTF-8
.相反,如果要在交互式环境中复制问题,则可以通过PYTHONLEGACYWINDOWSSTDIO
获得该行为。
何时何地设置此项取决于您的系统环境。其他应用程序是否依赖于较旧的脚本或其他版本的 Python 而不这样做?如果没有,您可以考虑设置全局系统环境变量。或者,您可以在执行脚本之前设置它,即在运行它的批处理文件中。
原因是我的LANG
环境变量设置为en_US
,而它应该被en_US.UTF-8
。
解决问题的另一种方法是将PYTHONENCODING
设置为UTF-8
(对我来说它是空的)。
我仍然不完全理解为什么 Python 只对非交互式脚本感到困惑......
更多详细信息: https://simulrpi.readthedocs.io/en/latest/display_problems.html