为什么locale.getpreferredencoding()返回' ANSI_X3.4-1968'而不是' UTF-8'?

时间:2017-06-03 13:27:44

标签: python encoding utf-8

每当我尝试读取UTF-8编码的文本文件时,即使使用open(file_name, encoding='utf-8'),我总是会收到错误,说ascii编解码器无法解码某些字符(例如,使用for line in f: print(line)时)

Python 3.5.3 (default, Jan 19 2017, 14:11:04)
[GCC 6.3.0 20170118] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import locale
>>> locale.getpreferredencoding()
'ANSI_X3.4-1968'
>>> import sys
>>> sys.getfilesystemencoding()
'ascii'
>>>

locale命令打印:

locale: Cannot set LC_CTYPE to default locale: No such file or directory
locale: Cannot set LC_ALL to default locale: No such file or directory
LANG=en_US.UTF-8
LANGUAGE=
LC_CTYPE=en_HK.UTF-8
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
LC_ALL=

3 个答案:

答案 0 :(得分:2)

我有类似的问题。对我来说,最初没有设置环境变量LANG(您可以通过运行env进行检查)

$ python3 -c 'import locale; print(locale.getdefaultlocale())'
(None, None)
$ python3 -c 'import locale; print(locale.getpreferredencoding())'
ANSI_X3.4-1968

对我而言,可用的语言环境是(在新的Ubuntu 18.04 Docker映像上):

$ locale -a
C
C.UTF-8
POSIX

所以我选择了utf-8:

$ export LANG="C.UTF-8"

然后一切正常

$ python3 -c 'import locale; print(locale.getdefaultlocale())'
('en_US', 'UTF-8')
$ python3 -c 'import locale; print(locale.getpreferredencoding())'
UTF-8

如果您选择了一个不可用的语言环境,例如

export LANG="en_US.UTF-8"

它将起作用:

$ python3 -c 'import locale; print(locale.getdefaultlocale())'
('en_US', 'UTF-8')
$ python3 -c 'import locale; print(locale.getpreferredencoding())'
ANSI_X3.4-1968

这就是locale给出错误消息的原因:

locale: Cannot set LC_CTYPE to default locale: No such file or directory
locale: Cannot set LC_ALL to default locale: No such file or directory

答案 1 :(得分:0)

我通过运行以下命令解决了该问题:

apt install locales-all

答案 2 :(得分:-2)

我认为您误读了错误消息。小心区分 Unicode De codeError Unicode En codeError

你说Python抱怨“ascii编解码器无法解码某些字符”。但是,据我所知,没有这样的错误信息。比较以下两种情况:

>>> b = 'é'.encode('utf8')
>>> b.decode('ascii')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can’t decode byte 0xc3 in position 0: ordinal not in range(128)
>>> 'é'.encode('ascii')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can’t encode character '\xe9' in position 0: ordinal not in range(128)

要么“不能解码字节”或“不能编码字符”,但它永远不会“无法解码字符”。

这可能看起来很迂腐,但在这一行中,

for line in f: print(line)

您有 de 编码(冒号前)和 en 编码(print表达式)。因此,您需要确定哪个进程导致了问题。一种可能性是将其写成两行。

但是,如果f在您编写时使用encoding='utf-8'打开,那么我很确定问题是由print表达式引起的。 默认情况下,print()会写入sys.stdout。由于此流在启动Python时已经打开,因此其编码也已设置 - 具体取决于您的环境。由于在您的语言环境中未设置LC_ALL,因此使用ASCII默认值(“ANSI X3.4-1968”)(这可能会在标题中回答您的问题)。

如果您不能或不想更改区域设置,可以执行以下操作:将Python中的UTF-8文本发送到STDOUT:

  • 使用基础二进制流:

    for line in f:
        sys.stdout.buffer.write(line.encode('utf-8')
    
  • 重新编码sys.stdout(实际上:使用重新编码的版本替换 sys.stdout):

    import codecs
    sys.stdout = codecs.getwriter('utf-8')(sys.stdout.buffer)
    

在任何情况下,您的终端仍然无法正确显示UTF-8文本,因为它无法正常显示,或者因为没有配置这样做。在这种情况下,你可能会看到问号或mojibake。但这是一个不同的故事,超出了Python的控制......