【问题标题】:Python UTF8 string confusionPython UTF8 字符串混淆
【发布时间】:2011-03-14 08:28:09
【问题描述】:

我已经对此有一段时间了,我已经阅读了很多文章,但问题并没有更清楚。我有一堆字符串存储在我的数据库中,想象一下:

x = '\xd0\xa4'
y = '\x92'

在 Python shell 中,我得到以下信息:

print x
Ф
print y
?

这正是我想看到的。但是有以下内容:

print unicode(x, 'utf8')
Ф

但不是这个:

unicode(y, 'utf8')
UnicodeDecodeError: 'utf8' codec can't decode byte 0x92 in position 0: unexpected code byte

我的感觉是我们的字符串被破坏了,因为 Django 试图将它们转换为 unicode,但我只是在猜测这一点。任何见解或解决方法都值得赞赏。

UPDATE:当我查看包含 '\x92' 值的行的数据库时,我看到这个字符为 '。一个撇号。我正在使用 Unicode UTF-8 编码查看数据库的内容。

【问题讨论】:

  • y 不是有效的 UTF-8 编码字符串。为什么你希望 Python 能够解码?
  • 另外,我假设x = '\xd0\xa4(有一个额外的斜线)
  • @Thanatos。我知道。但是如何在不指定编码的情况下打印呢?可以推断编码吗?
  • 顺便说一句,x = '\xd0\xa4' 可能会做一些与从数据库加载字符串完全不同的事情。
  • 下次不要自作聪明,把你的问题减少到无关的事情上,谢谢。

标签: python django unicode


【解决方案1】:

看起来你有错字;应该是x = '\xd0\xa4'。如果您使用实际运行的内容和输出中出现的内容的复制粘贴,这将非常有帮助。

"\x92" 不是有效的 UTF-8 字符串。这解释了您遇到的异常。

更多的谜团是为什么print y 产生?。你叫什么“Python控制台”?它似乎以“替换”模式运行并替换“?” ...你确定这是一个简单的“?”而不是白色的“?”在黑钻石里面?为什么说“?”正是您期望看到的?

更新:您现在说“”“当我查看包含 '\x92' 值的行的数据库时,我将此字符视为 '。一个撇号。我正在查看使用 Unicode UTF-8 编码的数据库内容。"""

这不是撇号。似乎该数据段已使用 cp125X(又名 windows-125X)编码之一进行编码。使用 cp1252 说明(通常的嫌疑人):

IDLE 2.6.4      
>>> import unicodedata
>>> uc = '\x92'.decode('cp1252')
>>> print repr(uc)
u'\u2019'
>>> print uc
’
>>> unicodedata.name(uc)
'RIGHT SINGLE QUOTATION MARK'
>>> 

不要“使用 Unicode UTF-8 编码查看数据库的内容”(不管是什么意思),尝试编写一个小的 Python 代码 sn-p 来提取有问题的字符串,然后执行 print repr(bad_string)。向我们展示您运行的代码,以及 repr() 的输出。还告诉我们哪个版本的 Python,什么平台(基于 Windows 或 unix),以及什么版本的数据库软件。以及与相关列相关的 CREATE TABLE 语句部分。

另请阅读thisthis

【讨论】:

  • 我不是告诉过你这会发生吗? :)
  • @hop:不,您说您怀疑存在不同的潜在问题。这就像说你怀疑太阳从东方升起——一个 OP 很少问他们应该问的问题。
【解决方案2】:

\x92 不是有效的 utf-8 编码字符。

您不会注意到这一点,因为您对 xy 使用简单(非 unicode)字符串,直到您尝试将它们解码为 un​​icode 字符串。然后,当您打印它们时,它们会“按原样”简单地转储到终端,并且终端本身会根据其编码设置来解释字节。

unicode() 的第三个参数告诉 python 在编码(解码)错误的情况下该怎么做:

>>> unicode('\x92', 'utf8', 'replace')
u'\ufffd'
>>> print _
�

【讨论】:

  • @hop:“您不会注意到这一点,因为您对 x 和 y 使用简单(非 unicode)字符串,直到您尝试将它们解码为 un​​icode 字符串。” -- 所以你是说简单的非 unicode 字符串“\xd0\xa4”已经神奇地转换成 unicode 字符 U+0424 CYRILLIC CAPITAL LETTER EF 而没有发生任何解码??
  • @John:不,我根本没有这么说。终端将有效的 utf-8 序列解码为要显示的 unicode 字符并没有什么神奇之处。只是不是 python 可以进行任何解码。
  • @John:终端将“\xd0\xa4”解码为 U+0424,因为您的终端配置为 UTF-8,这通常是当今的默认设置。如果它被设置为其他东西,这将不起作用。
  • @hop:问题的本质是在这种情况下“终端”解码字节串的方式与unicode(y, 'utf8')不一致。
  • @Thanatos:我很清楚 utf8 通常是默认值(对于 *x 终端)。我的意思是,@hop 的原文似乎是在说终端根本没有进行任何解码。
【解决方案3】:

我认为除 ASCII 子集之外的任何 unicode 字符在 UTF-8 中都有多字节表示。您的 y 作为一个单字节每字符的字符串是有意义的,但不是一个 UTF-8 字符串。因为单个字节在 0x00 到 0x7F ASCII 范围之外,所以编解码器需要一个额外的字节或更多字节来转换为“真正的”unicode 字符。

不过,我不像以前那样熟悉 Python,而且我对这个答案没有信心。

EDIT 跳跃是 IMO 更好的答案。

【讨论】:

    【解决方案4】:

    我现在明白你的困惑所在了。让我们看看这个:

    x = '\xd0\xa4'
    y = '\x92'
    

    如果我print x,我会得到Ф。这是因为我的终端使用 UTF-8 作为其字符编码。因此,当它得到D0 A4 时,它会尝试将其解码为UTF-8,并得到一个“Ф”。如果我将终端更改为使用 ISO-8859-1(“latin1”),并且我说 print x,我的终端将尝试使用 ISO-8859-1 解码 D0 A4,因为 D0 A4也是一个有效的 ISO-8859-1 字符串,它会解码,但这次是“Ф”。

    现在,为print y。这不是 UTF-8 字符串,所以我的终端无法解码。在我的情况下,它通过打印“�”向我显示了这个错误。我想知道您是否看到“�”或“?” - 你可能应该看到前者,但这取决于你的终端在面对糟糕的输出时会做什么。

    您的终端的编码应该匹配$LANG 所说的任何内容,并且您的程序应该以$LANG 指定的任何编码输出数据。如今,$LANG 通常是???.UTF-8,其中??? 会有所不同。 (我的是en_US.UTF-8

    现在,当您说unicode(y, 'utf8') 时,Python 会尝试将其解码为 UTF-8,并适当地引发异常。

    我正在使用 Gnome 终端,可以通过转到终端 → 设置字符编码来更改我的字符编码

    【讨论】:

      【解决方案5】:
      0x92 (hex) = 10 010010 (binary)
      

      由于 UTF-8 可以在一个字节中表示 010010,因此“标头”必须是 0(--> 00010010)而不是 10(它永远不能是第一个字节的标头)。字符可能不会用比需要更多的字节来表示,因此“\x92”不是有效的 UTF-8 编码字符串。

      我猜您的数据库使用了一些每个字符一个字节的编码(例如 latin-1)。如果您自己编写数据库查询,则必须确保连接编码正确或字符串解码正确。使用 Django 模型,一切都应该自动运行。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-11-02
        • 1970-01-01
        • 2013-11-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-05-16
        • 1970-01-01
        相关资源
        最近更新 更多