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