Python UTF8字符串混乱

时间:2010-07-10 21:48:06

标签: python django unicode

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

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编码查看数据库的内容。

5 个答案:

答案 0 :(得分:7)

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

“\ x92”不是有效的UTF-8字符串。这解释了你得到的例外。

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

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

那不是撇号。看来这段数据是使用cp125X(aka 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代码来提取有问题的字符串,然后执行print repr(bad_string)。向我们展示您运行的代码,以及repr()的输出。还告诉我们哪个版本的Python,什么平台(基于Windows或unix),以及什么版本的数据库软件。并且CREATE TABLE语句的部分与相关列相关。

另请阅读thisthis

答案 1 :(得分:5)

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

您没有注意到,因为您使用xy的简单(非unicode)字符串,直到您尝试将它们解码为unicode字符串。当你打印它们时,它们被简单地“按原样”转储到终端,终端本身根据其编码设置解释字节。

unicode()有第三个参数告诉python在编码(解码)错误时该怎么做:

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

答案 2 :(得分:4)

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

我对Python的熟悉程度与以前不同,我对这个答案并不自信。

EDIT hops是IMO的最佳答案。

答案 3 :(得分:2)

我现在看到你感到困惑的地方。我们来看看:

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

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

现在,print y。这不是UTF-8字符串,所以我的终端无法对此进行解码。在我的例子中,它通过打印“ ”向我显示了这个错误。我想知道你是否看到“ ”或“?” - 您应该看到前者,但这取决于您的终端在输出不良时的作用。

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

现在,当你说en_US.UTF-8时,Python会尝试将其解码为UTF-8,并适当地抛出异常。

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

答案 4 :(得分:1)

0x92 (hex) = 10 010010 (binary)

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

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