我在使用Python 3时面临一个非常奇怪的问题:
>>> a = "abcé"
>>> a
'abcé'
>>> print(a)
abcé
>>> print(a.lower())
abc�
我不知道它来自哪里,但它无法小写unicode字符。请注意,我无法在任何地方重现该错误,这只是在我的一台计算机上,我得到以下问题。此外,同一台计算机上的python2正确打印abcé
。
此外,a.upper()
正在返回ABCé
而不是ABCÉ
,因此它不会遇到与lower
相同的问题。
有什么想法吗?
答案 0 :(得分:3)
我不完全确定这里发生了什么,但它看起来像是python和终端之间的一些编码问题。
最佳做法是将所有内容保存在'utf-8'中,以确保您可以在python中执行import locale
和locale.setlocale(locale.LC_ALL, 'en_US.UTF-8')
或执行export LC_ALL=en_US.utf8
。
我对这些编码主题不是100%流利,所以也许编码专家有更好的答案/解释,但这应该可以解决你的问题。
答案 1 :(得分:2)
您观察到的行为确实非常奇怪:字母“é”不受Python str.upper()
的影响,而是由str.lower()
变成替换字符。
同样奇怪的是,这似乎取决于环境,因为所述str
方法没有表现出任何定位(即使这有时可能有意义,如土耳其“i”映射的情况→“İ”和“ı”→“I”),但始终使用Unicode的默认大小写算法。
这种奇怪现象最可能的解释是Python不会“看到”与您相同的数据。 正如Hansaplast在他们的回答中所写,终端和Python解释器之间可能存在编码不匹配。 人们通常不必关心这一点,但是当你使用交互式解释器时,显示打字和打印字符的工作实际上并不是由Python执行的,而是由终端[模拟器]执行的,而这个附加层可以是有时会出现问题。
那到底是怎么回事? 我相信以下场景可以解释观察到的行为:
C3 A9
发送到Python。当它从Python接收C3 A9
时,它将显示“é”。locale.getlocale()
的返回值确认。当它收到C3 A9
时,会将其解码为“Ô,这是mojibake的常见情况。关于这种错误配置的真正令人讨厌的事情是它只在某些情况下可见。
由于en- / decode的对称性,即使非ASCII字符也可能未被注意到。
如果Python简单地回应它的输入,即。打印“é”,这将被终端解除为“é”,因此隐藏了错误。
但是,当以某种方式解释单个字符时 - 与str.upper()
和lower()
一样 - 可能会发生意外情况。
在你的情况下,.upper()
没有效果,因为“Ô已经是大写,“©”是无壳的。这就是'abcé'.upper()
在屏幕上显示'ABCé'
的原因。
但是,lowercasing产生“ã©”,Python将其编码为E3 A9
。
由于这不是有效的UTF-8字节序列,因此终端在解释时失败并显示替换字符( )。
如果这种解释是正确的,那么如何解决编码配置错误?
对于交互式会话,Python使用LC_ALL
等环境变量来设置STDIN / STDOUT的编码可能是有意义的。像export LC_ALL=en_US.utf8
这样的行在您的终端中运行的shell的启动脚本中放置,例如。 的.bashrc 。从Python中更改语言环境无效,因为STD-stream编码在启动时设置,在您调用locale.selocale()
时不会更新。
对于脚本,您可能不想依赖环境变量。
您可以围绕每个标准渠道的二进制流创建新的io.TextIOWrapper
:
sys.stdin = open(sys.stdin.buffer.fileno(), encoding='utf8')
sys.stdout = open(sys.stdout.buffer.fileno(), 'w', encoding='utf8')
sys.stderr = open(sys.stderr.buffer.fileno(), 'w', encoding='utf8')
(我不推荐这种解决方案用于交互式会话。特别是如果你输错了某些内容,你可能会遇到难以恢复的情况。)