UTF-8编码下的“破损”Unicode字符串?

3

我已经研究了Unicode及其Python实现两天了,我认为我正在逐渐了解它是什么。为了更有信心,我想确认一下当前问题的假设是否正确。

在Django中,表单向我提供unicode字符串,我怀疑这些字符串可能是"损坏的"。在Python中,Unicode字符串应该编码为UTF-8,对吗?在将字符串"fähre"输入文本字段后,浏览器会在POST请求中发送字符串"f%c3%a4hre"(通过wireshark检查)。但是,当我通过form.cleaned_data检索值时,我得到了字符串u'f\xa4hre'(请注意它是一个Unicode字符串)。据我所理解的是,那是ISO-8859-1编码的Unicode字符串,这是不正确的。正确的字符串应该是u'f\xc3\xa4hre',这将是一个UTF-8编码的Unicode字符串。这是Django的一个错误还是我的理解存在问题?

为了解决这个问题,我编写了一个函数来处理Django表单中的任何文本输入:

def fix_broken_unicode(s):
    return unicode(s.encode(u'utf-8'), u'iso-8859-1')

它是做什么的

>>> fix_broken_unicode(u'f\xa4hre')
u'f\xc3\xa4hre'

对我来说,这似乎不太优雅,但将Django的settings.DEFAULT_CHARSET设置为'utf-8'没有帮助,也没有其他任何帮助。我试图在整个应用程序中使用unicode,以便以后不会出现任何奇怪的错误,但显然仅标记所有字符串为u'...'是不够的。

编辑:考虑到Dirk和sth的答案,我现在将字符串按原样保存到数据库中。真正的问题是我试图对这些字符串进行urlencode,以便将它们用作Twitter API等的输入。然而,在GET或POST请求中,显然需要UTF-8编码,而标准的urllib.urlencode()函数无法正确处理它(会抛出异常)。请查看我在pastebin中的解决方案,并随时发表评论。


你不能将Unicode字符串“原样”保存到数据库中,因为(正如某人所解释的那样),Unicode字符串实际上并没有被编码成任何东西,你需要对它们进行编码才能将其存储到数据库中。 - user3850
此外,urllib.urlencode() 可能比你更正确。为什么不向我们展示你想要做什么以及实际的异常是什么呢? - user3850
@hop 我无法提供完整的示例,因为它相当复杂且分散在一堆类中。此外,我正在使用数据的HTTP请求需要Twingly API的付费帐户,这是一个社交媒体搜索索引,因此我无法在此处提供工作URL。我将尝试在即将到来的周末编写一个等效的示例。 - fqxp
因此,你应该将代码最小化以展示问题,这样你很可能会自己解决它。 - user3850
2个回答

4
u'f\xa4hre'是一个Unicode字符串,没有被编码成任何格式。Unicode代码点0xa4代表字符ä。虽然在ISO-8859-1中,ä也可以被编码成字节0xa4,但这并不重要。
Unicode字符串可以包含任何Unicode字符,无需以某种方式对其进行编码。例如,轮渡可以表示为u'\u8f6e\u6e21',它们只是两个Unicode代码点。UTF-8编码会更长,为'\xe8\xbd\xae\xe6\xb8\xa1'
因此,不需要修复编码问题,您只是看到了Unicode字符串的内部表示。

如果一个模块返回像 u'\xe8\xbd\xae\xe6\xb8\xa1' 这样的东西,那不是不正确的吗? - endolith
1
@endolith:它并不严格无效,但是是无意义的(u'轮渡'),显然不是预期的u'轮渡' - Mechanical snail

1

不完全准确:解码后的Unicode字符串是unicode,这意味着它可能包含代码超过255的字符。解释器如何表示这些字符取决于平台,但通常现在使用至少16位宽度的字符元素。ISO-8859-1是Unicode的一个合适子集。因此,字符串u'f\xa4hre'实际上是正确的--\xa4是一个渲染工件,因为Python不知道何时可以安全地在控制台上包含代码超出某个范围的字符。

UTF-8是一种传输编码,即一种特殊的方式来编写Unicode数据,使其可以存储在每个字符/字节具有8位宽度的“通道”中。为了计算Unicode字符串的正确“外部”(或传输)编码,您将使用encode方法,传递所需的表示形式。它返回一个正确编码的字节串(而不是Unicode字符串)。

反向转换是decode,它接受一个字节串和一个编码名称,并产生一个Unicode字符串


注意:“解码后,Unicode字符串是Unicode格式,这意味着它可能包含代码超过255的字符。” Unicode字符具有代码点和名称,而不是数字。 Unicode字符是高级对象。如果您对此对象进行序列化,则必须为该字符指定一个数字。 - guettli

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接