小写时python3 unicode失败

时间:2017-12-20 14:55:36

标签: python string python-3.x unicode lowercase

我在使用Python 3时面临一个非常奇怪的问题:

>>> a = "abcé"
>>> a
'abcé'
>>> print(a)
abcé
>>> print(a.lower())
abc�

我不知道它来自哪里,但它无法小写unicode字符。请注意,我无法在任何地方重现该错误,这只是在我的一台计算机上,我得到以下问题。此外,同一台计算机上的python2正确打印abcé

此外,a.upper()正在返回ABCé而不是ABCÉ,因此它不会遇到与lower相同的问题。

有什么想法吗?

2 个答案:

答案 0 :(得分:3)

我不完全确定这里发生了什么,但它看起来像是python和终端之间的一些编码问题。

最佳做法是将所有内容保存在'utf-8'中,以确保您可以在python中执行import localelocale.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执行的,而是由终端[模拟器]执行的,而这个附加层可以是有时会出现问题。

那到底是怎么回事? 我相信以下场景可以解释观察到的行为:

  • 您的终端配置为使用UTF-8。当您键入“é”时,它会将字节C3 A9发送到Python。当它从Python接收C3 A9时,它将显示“é”。
  • 然而,Python使用Latin-1,因为您通过locale.getlocale()的返回值确认。当它收到C3 A9时,会将其解码为“Ô,这是mojibake的常见情况。
  • UTF-8和Latin-1都是ASCII的超集,因此只要您只使用ASCII字符,这种错误配置就不是问题。当您键入“A”时,Python读取“A”,输出相同。

关于这种错误配置的真正令人讨厌的事情是它只在某些情况下可见。 由于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')
    

    (我不推荐这种解决方案用于交互式会话。特别是如果你输错了某些内容,你可能会遇到难以恢复的情况。)