【问题标题】:Python3 utf-8 decode issuePython3 utf-8 解码问题
【发布时间】:2018-06-06 16:55:56
【问题描述】:

以下代码在我的 Windows 机器上使用 Python3 可以正常运行并打印字符“é”:

data = b"\xc3\xa9"

print(data.decode('utf-8'))

但是,在基于 Ubuntu 的 docker 容器上运行相同的结果:

UnicodeEncodeError: 'ascii' codec can't encode character '\xe9' in position 0: ordinal not in range(128)

是否需要安装任何东西才能启用 utf-8 解码?

【问题讨论】:

  • 指定将给定字符串解码为 'utf-8' 无论如何都应该有效。只有当我明确指定'ascii'作为编解码器时,我才会得到你引用的错误。您的错误还暗示正在使用 ascii。我知道没有 linux 多年来默认使用 utf-8 以外的任何东西....
  • @planetmaker:Linux 肯定有一些“最小”设置默认为LANG=C,其中print,而不是decode,会有问题。在相关的 shell 初始化文件中显式更改为 LANG=en_US.utf-8(并注销然后重新登录以确保在任何地方都正确设置了区域设置)应该可以修复它。
  • @ShadowRanger 至少在 Ubuntu Xenial 上没有。我从一开始就一直使用语言环境 lv_LV.Utf-8,但 python 默认为 ascii。最近才在尝试在 CLI 中输入 unicode 时发现。在文件中总是通过注释指定编码。

标签: python linux windows python-3.x utf-8


【解决方案1】:

似乎 ubuntu - 取决于版本 - 默认使用一种编码或另一种编码,它也可能在 shell 和 python 之间有所不同。来自this postingthis blog

因此推荐的方法似乎是告诉你的python实例使用utf-8作为默认编码:

通过环境变量设置python源文件的默认编码:

export PYTHONIOENCODING=utf8

此外,在您的源文件中,您可以明确说明您希望使用的编码,因此无论环境设置如何,它都应该可以工作(请参阅this question + answerpython docsPEP 263

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
....

关于python读取文件编码的解释,可以在open命令中明确指定

with open(fname, "rt", encoding="utf-8") as f:
    ...

还有一种更骇人听闻的方法,但会产生一些副作用,但每次都要明确指定它

import sys
# sys.setdefaultencoding() does not exist, here!
reload(sys)  # Reload does the trick!
sys.setdefaultencoding('UTF8')

请阅读related answer 和 cmets 中有关此 hack 的警告。

【讨论】:

  • 不知道为什么这被否决了,这正是我最近遇到的情况(Ubuntu shell 使用 utf8,但 python 命令行解释器使用 ascii)。请注意,在 python 代码(文件)中,您不需要使用 sys,只需在文件开头通过注释指定编码即可。
  • 谢谢,@Gnudiff - 我在答案中添加了这种方式
  • 请注意源标题中的# coding: utf8 行和sys.setdefaultencoding 做不同的事情。第一个是关于 Python 解释器如何处理源代码中的字符串文字。当您open() 一个文件而不明确指定编解码器时,第二个会影响默认编码。您还应该知道 setdefaultencoding 技巧是一种 hack,并且可能会产生不良的副作用(我忘记了具体是什么,因为我从不使用它)。最好始终使用open(fn, encoding=...)
  • @lenz 当然......也应该添加这一点。并且也进行了修改。谢谢!
【解决方案2】:

问题在于print() 表达式,而不是decode() 方法。 如果仔细观察,引发的异常是 UnicodeEncodeError,而不是 -DecodeError。

每当您使用print() 函数时,Python 都会将其参数转换为str,然后将结果编码为bytes,然后将其发送到终端(或运行Python 的任何内容)。 用于编码的编解码器(例如 UTF-8 或 ASCII)取决于环境。 理想情况下,

  • Python 使用的编解码器与终端期望的编解码器兼容,因此字符显示正确(否则您会得到类似“é”而不是“é”的 mojibake);
  • 使用的编解码器涵盖的字符范围足以满足您的需求(例如 UTF-8 或 UTF-16,其中包含所有字符)。

在您的情况下,您提到的 Linux docker 不满足第二个条件:使用的编码是 ASCII,它只支持在旧英文打字机上找到的字符。 以下是解决此问题的几个选项:

  • 设置环境变量:在 Linux 上,Python 的编码默认值取决于此(至少部分)。以我的经验,这有点反复试验。将LC_ALL 设置为包含“UTF-8”的内容曾经对我有用。您必须将它们放在终端运行的 shell 的启动脚本中,例如。 .bashrc.
  • 重新编码STDOUT,像这样:

    sys.stdout = open(sys.stdout.buffer.fileno(), 'w', encoding='utf8')
    

    使用的编码必须与终端匹配。

  • 自己编码字符串并将它们发送到底层sys.stdout 的二进制缓冲区,例如。 sys.stdout.buffer.write("é".encode('utf8'))。这当然比print("é") 更多样板。同样,使用的编码必须与终端匹配。
  • 完全避免print()。使用open(fn, encoding=...) 输出,日志模块用于进度信息——取决于你的脚本的交互性,这可能是值得的(诚然,在使用日志模块写入 STDERR 时,你可能会遇到同样的编码问题)。李>

可能还有其他选择,但我怀疑是否有更好的选择。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-06-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-26
    • 1970-01-01
    • 2018-02-18
    • 1970-01-01
    相关资源
    最近更新 更多