【问题标题】:Why is sys.getdefaultencoding() different from sys.stdout.encoding and how does this break Unicode strings?为什么 sys.getdefaultencoding() 与 sys.stdout.encoding 不同,这如何破坏 Unicode 字符串?
【发布时间】:2013-03-09 23:10:46
【问题描述】:

我花了几个小时寻找 Unicode 字符串的问题,这些字符串被分解为 Python (2.7) 对我隐藏的东西,但我仍然不明白。首先,我尝试在我的代码中始终使用u".." 字符串,但这导致了臭名昭著的UnicodeEncodeError。我尝试使用.encode('utf8'),但这也无济于事。最后,事实证明我不应该使用任何一个,这一切都会自动进行。然而,我(在这里我需要感谢帮助我的朋友)在我的头撞到墙上时确实注意到了一些奇怪的事情。 sys.getdefaultencoding() 返回 ascii,而 sys.stdout.encoding 返回 UTF-8。 1. 在下面的代码中可以正常工作,无需对 sys 进行任何修改,并且 2. 引发 UnicodeEncodeError。如果我用reload(sys).setdefaultencoding("utf8") 更改默认系统编码,那么 2. 工作正常。我的问题是为什么这两个编码变量首先是不同的,我如何设法在这段简单的代码中使用错误的编码?请不要把我发给Unicode HOWTO,我在关于UnicodeEncodeError 的数十个问题中显然已经读到了这一点。

#  -*- coding: utf-8 -*-
import sys


class Token:
    def __init__(self, string, final=False):
        self.value = string
        self.final = final

    def __str__(self):
        return self.value

    def __repr__(self):
        return self.value

print(sys.getdefaultencoding())
print(sys.stdout.encoding)

# 1.
myString = "I need 20 000€."
tok = Token(myString)
print(tok)

reload(sys).setdefaultencoding("utf8")

# 2.
myString = u"I need 20 000€."
tok = Token(myString)
print(tok)

【问题讨论】:

  • 我的问题不是关于问题的解决方案,而是关于它的原因。我知道如何解决它,而且我知道它在 Python 3.0 中不存在。你问什么无关紧要。

标签: python stdout utf sys


【解决方案1】:

我的问题是为什么这两个编码变量首先是不同的

它们有不同的用途。

sys.stdout.encoding 应该是您的终端用来解释文本的编码,否则您可能会在输出中看到 mojibake。在一个环境中可能是 utf-8,在另一个环境中可能是 cp437,等等。

sys.getdefaultencoding() 在 Python 2 上用于隐式转换(当未明确设置编码时),即 Python 2 可以将纯 ascii 字节串和 Unicode 字符串混合在一起,例如,xml.etree.ElementTree 将 ascii 范围内的文本存储为字节串或json.dumps() returns an ascii-only bytestring instead of Unicode in Python 2 — 可能是由于性能的原因 — 在表示 ascii 字符方面,字节比 Unicode 便宜。 Python 3 中禁止隐式转换。

sys.getdefaultencoding() 在 Python 2 中的所有系统上始终为 'ascii',除非您不应该这样做覆盖它,否则它可能会隐藏错误,并且由于使用可能错误的编码的隐式转换,您的数据可能很容易损坏数据。

顺便说一句,还有另一种常见的编码sys.getfilesystemencoding() 可能与两者不同。 sys.getfilesystemencoding() 应该是用于对 OS 数据(文件名、命令行参数、环境变量)进行编码的编码。

使用# -*- coding: utf-8 -*- 声明的源代码编码可能与所有已经提到的编码不同。

当然,如果你从文件中读取数据,网络;它可能使用与上述不同的字符编码,例如,如果在记事本中创建的文件使用 Windows ANSI 编码(如cp1252)保存,那么在另一个系统上,所有标准编码都可能与它不同。

重点是:由于与 Python 无关的原因,可能存在 多种 编码,为了避免头痛,请使用 Unicode 表示文本:尽快转换编码文本输入到 Unicode,并在输出时尽可能晚地将其编码为字节(可能使用不同的编码)——这就是所谓的Unicode sandwich 的概念。

如何在这段简单的代码中使用错误的编码?

  1. 您的第一个代码示例不好。您在 Python 2 上的字节字符串中使用了不应该使用的非 ascii 文字字符。仅将字节串的文字用于二进制数据(或必要时称为本机字符串)。如果您在任何不使用 utf-8 兼容编码的环境(如 Windows 控制台)中使用 Python 2 运行该代码,则该代码可能会产生诸如 I need 20 000Γé¼. 之类的 mojibake(注意字符噪音)

  2. 假设reload(sys) 不是其中的一部分,第二个代码示例是可以的。如果您不想在所有字符串文字前加上 u'';你可以使用from __future__ import unicode_literals

您的实际问题是 UnicodeEncodeError 错误,reload(sys) 不是正确的解决方案!
正确的解决方案是configure your locale properly on POSIX (LANG, LC_CTYPE) 或set PYTHONIOENCODING envvar if the output is redirected to a pipe/file or install win-unicode-console to print Unicode to Windows console。

【讨论】:

    【解决方案2】:

    我注意到一些标准代码(邮递员库)的相同行为。 感谢您的分析,它帮助我节省了一些时间。 :-) 问题是完全一样的。我的系统使用sys.getdefaultencoding() 并得到ascii,这不适合处理包含 1000 个 UTF-8 编码名称的列表。

    一方面 stdin/stdout 甚至文件系统编码 (utf-8) 和另一方面 (ascii) 的“默认编码”之间存在不匹配。这个线程:How to print UTF-8 encoded text to the console in Python < 3? 似乎表明它是众所周知的,Changing default encoding of Python? 包含一些迹象表明更同质的(如“utf-8无处不在”)会破坏其他东西,如散列实现。

    因此,更改默认编码也不简单。 (请参阅http://blog.ianbicking.org/illusive-setdefaultencoding.html 了解执行此操作的各种方法。)它已从site.py 文件中的sys 实例中删除。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-04-10
      • 2021-07-26
      • 2013-03-30
      • 2011-07-10
      • 1970-01-01
      • 2012-08-13
      • 2011-08-27
      • 1970-01-01
      相关资源
      最近更新 更多