Выход Python zlib, как восстановить из таблицы mysql utf-8? - PullRequest
7 голосов
/ 25 октября 2009

В python я сжал строку, используя zlib, а затем вставил ее в столбец mysql типа blob, используя кодировку utf-8. Строка возвращается как 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' не может кодировать символ u '\ x9c' в позиции 1: порядковый номер не в диапазоне (128)

если я сделаю это, он вставит несколько дополнительных символов:

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

Есть предложения?

Ответы [ 3 ]

7 голосов
/ 25 октября 2009

Unicode совместим с латиницей-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'

РЕДАКТИРОВАТЬ: Исправлена ​​опечатка, латинский-1 не предназначен для совместимости с Unicode, это наоборот.

2 голосов
/ 25 октября 2009

У вас есть объект Unicode , который действительно кодирует байты. Это прискорбно, поскольку строки юникода должны действительно кодировать текст, верно?

В любом случае, мы хотим создать байтовую строку ... это str в Python 2.x. По напечатанной вами строке u'x\x9cK\x04\x00\x00b\x00b' мы видим, что значения байтов кодируются как кодовые точки Unicode. Мы можем получить числовое значение кодовой точки с помощью функции 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. Учитывая, конечно, что символ находится в диапазоне от 0 до 255, где определено Latin-1. Это означает, что мы можем сделать преобразование байтов непосредственно с Latin-1:

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

Что, как вы видите, дает тот же результат.

1 голос
/ 25 октября 2009
valueInserted = zlib.compress('a') = 'x\x9cK\x04\x00\x00b\x00b'

Обратите внимание, что это объект str . Вы говорите, что «вставили его в столбец mysql типа blob, используя кодировку utf-8». Поскольку сжатая строка является двоичной, а не текстовой, столбец blob - это соответствующий тип столбца, но ЛЮБОЕ кодирование или другое преобразование - очень плохая идея. Вы должны иметь возможность восстанавливать из базы данных ТОЧНО, вплоть до последнего вставленного вами бита, в противном случае распаковка не удастся, либо из-за возникновения ошибки, либо (менее вероятно, но хуже), производящей мусор в режиме без вывода сообщений.

Вы говорите, что возвращаетесь после любого процесса, который вы проходите, вставляя и извлекая его снова:

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

Обратите внимание, что есть только одно крошечное визуальное отличие: «что-то» вместо «чего-то». Это делает его юникодом объектом. Судя по вашим собственным свидетельствам, «возвращается как UTF-8» не правильно. Объект Unicode и объект str, закодированные в utf8, - это не одно и то же.

Угадай 1: вставить в виде необработанной строки, извлечь с декодированием из латиницы 1.

Предположим, что 2: вставить как сжатый код ('latin1'). Кодировать ('utf8'), извлечь с декодированием utf8.

Вы действительно должны понимать процесс вставки и извлечения, включая то, что кодирование и декодирование происходит по умолчанию.

Тогда вам действительно нужно исправить свой код. Однако, тем временем вы, вероятно, можете запутаться в том, что у вас есть.

Обратите внимание:

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

Проведите несколько испытаний с более сложным вводом, чем «а». Если, как я полагаю, вы видите, что все символы Юникода имеют порядковый номер в диапазоне (256), то у вас есть простой ключ:

>>> 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') слишком похоже на voodoo.

Если вышеперечисленное не работает (т.е. некоторые порядковые номера не находятся в диапазоне (256)), то вам нужно будет создать небольшой исполняемый скрипт, который точно показывает и воспроизводит, как вы сжимаете, вставляя в базу данных и извлекая ее из базы данных ... разбросайте множество print "variable", repr(variable) вокруг вашего кода, чтобы вы могли видеть, что происходит.

...