7

在 python 中,我使用 zlib 压缩了一个字符串,然后使用 utf-8 编码将其插入到 blob 类型的 mysql 列中。该字符串以 utf-8 形式返回,但尚不清楚如何将其恢复为可以解压缩的格式。这是一些伪输出:

valueInserted = zlib.compress('a') = 'x\x9cK\x04\x00\x00b\x00b'

valueFromSqlColumn = u'x\x9cK\x04\x00\x00b\x00b'

zlib.decompress(valueFromSqlColumn) UnicodeEncodeError: 'ascii' codec can't encode character u'\x9c' in position 1: ordinal not in range(128)

如果我这样做,它会插入一些额外的字符:

valueFromSqlColumn.encode('utf-8') = 'x\xc2\x9cK\x04\x00\x00b\x00b'

有什么建议么?

4

3 回答 3

7

Unicode 旨在与 latin-1 兼容,因此请尝试:

>>> import zlib
>>> u = zlib.compress("test").decode('latin1')
>>> u
u'x\x9c+I-.\x01\x00\x04]\x01\xc1'

接着

>>> zlib.decompress(u.encode('latin1'))
'test'

编辑:修正了错字,latin-1 的设计目的不是与 unicode 兼容,反之亦然。

于 2009-10-24T20:11:57.003 回答
2

你有一个真正编码字节的unicode 对象。这很不幸,因为 unicode 字符串实际上应该只是编码文本,对吧?

无论如何,我们要做的是构造一个字节字符串。这是strPython 2.x中的一个。我们从您提供的打印字符串中看到u'x\x9cK\x04\x00\x00b\x00b',字节值被编码为 un​​icode 代码点。我们可以使用函数来获取代码点的数值ord(..)。然后我们可以使用函数获取该数字的字节字符串表示形式chr(..)。让我们试试这个:

>>> ord(u"A")
65
>>> chr(_)
'A'

所以我们可以自己解码字符串:

>>> udata = u'x\x9cK\x04\x00\x00b\x00b'
>>> bdata = "".join(chr(ord(uc)) for uc in udata)
>>> bdata
'x\x9cK\x04\x00\x00b\x00b'

(等等,上面的代码是做什么的?连接的东西?我们首先要做的是在字符串中创建一个代码点列表:

>>> [ord(uc) for uc in udata]
[120, 156, 75, 4, 0, 0, 98, 0, 98]

然后我们将数字解释为字节,分别转换它们:

>>> [chr(ord(uc)) for uc in udata]
['x', '\x9c', 'K', '\x04', '\x00', '\x00', 'b', '\x00', 'b']

最后,我们""使用分隔符将它们连接起来"".join(list-of-strings)

等待结束..)

但是,cls 巧妙地指出,Latin-1 编码具有这样的属性:Latin-1 编码中字符的字节值等于 Unicode 中字符的代码点。当然,假设该字符在定义 Latin-1 的 0 到 255 范围内。这意味着我们可以直接使用 Latin-1 进行字节转换:

>>> udata = u'x\x9cK\x04\x00\x00b\x00b'
>>> udata.encode("latin-1")
'x\x9cK\x04\x00\x00b\x00b'

如您所见,它给出了相同的结果。

于 2009-10-24T23:32:26.387 回答
1
valueInserted = zlib.compress('a') = 'x\x9cK\x04\x00\x00b\x00b'

请注意,这是一个str对象。您说您“使用 utf-8 编码将其插入到 blob 类型的 mysql 列中”。由于压缩字符串是二进制的,而不是文本,“blob”是一种合适的列类型,但任何编码或其他转换都是一个非常糟糕的主意。您需要能够从数据库中完全恢复到您插入的最后一位,否则解压缩将失败,要么引发错误,要么(不太可能,但更糟)默默地产生垃圾。

你说你在插入它并再次提取它的任何过程之后回来是:

valueFromSqlColumn = u'x\x9cK\x04\x00\x00b\x00b'

请注意,只有一个微小的视觉差异:u'something' 而不是 'something'。这使它成为一个unicode对象。到目前为止,根据您自己的证据,“返回为 UTF-8”是不正确的。以 utf8 编码的 unicode 对象和 str 对象不是一回事。

猜测 1:作为原始字符串插入,使用 latin1 解码提取。

猜想2:插入为compressed.decode('latin1').encode('utf8'),用utf8解码提取。

您确实需要了解插入和提取的过程,包括默认情况下发生的编码和解码。

然后你真的需要修复你的代码。然而,与此同时,你可能会拼凑你所拥有的东西。

请注意:

>>> valueFromSqlColumn = u'x\x9cK\x04\x00\x00b\x00b'
>>> all(ord(char) <= 255 for char in valueFromSqlColumn)
True

用比“a”更复杂的输入做一些试验。如果,正如我猜的那样,你看到所有的 unicode 字符在 range(256) 中都有一个序数,那么你有一个简单的 kludge:

>>> compressed = valueFromSqlColumn.encode('latin1')
>>> compressed
'x\x9cK\x04\x00\x00b\x00b'
>>> zlib.decompress(compressed)
'a'

之所以可行,是因为 Latin1 编码/解码不会改变序数。您可以通过以下方式恢复原始压缩值:

>>> compressed2 = ''.join(chr(ord(uc)) for uc in valueFromSqlColumn)
>>> compressed2
'x\x9cK\x04\x00\x00b\x00b'
>>> compressed2 == compressed
True

如果您认为使用 .encode('latin1') 太像巫毒了。

如果上述方法不起作用(即某些序数不在范围内(256)),那么您将需要生成一个小的可运行脚本,该脚本准确且可重现地显示您如何压缩、插入数据库以及从数据库中检索...在你的代码周围撒上很多东西,print "variable", repr(variable)这样你就可以看到正在发生的事情。

于 2009-10-24T23:32:17.823 回答