Как считать символы вместо байтов?
У меня есть некоторые uuids, хранящиеся в базе данных в виде строк в кодировке base32 без дополнения. Они 26 символов в длину. Я пытаюсь извлечь их в Python 2.7.5 и преобразовать их в двоичные данные для другого хранилища данных. Проблема возникает с моей утилитой Python DB, интерпретирующей эти строки base32 как юникод с 2 байтами на символ. Вот код:
str = row.uuid
print type(str)
print "Padding {0} with length {1}, mod 8 is {2}".format(s, len(s), len(s) % 8)
str = str.ljust(int(math.ceil(len(str) / 8.0) * 8), '=')
print str
uuidbytes = base64.b32decode(str)
row.couponUuid = uuid.UUID(bytes=uuidbytes)
Выход такой:
<type 'unicode'>
Padding ANEMTUTPUZFZFH6ANXNW5IOI4U with length 52, mod 8 is 4
ANEMTUTPUZFZFH6ANXNW5IOI4U====
File "path/to/my/script.py", line 143
uuidbytes = base64.b32decode(str)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/base64.py", line 222, in b32decode
raise TypeError('Non-base32 digit found')
TypeError: Non-base32 digit found
И документы говорят, что ошибка TypeError может быть вызвана неправильным заполнением. Как видите, рассматриваемая строка имеет 26 символов, а не 52, и, как таковая, получает только 4 =
для заполнения вместо 6, который требуется.
Если я попробую это в консоли с вставкой в той же строке, это будет работать, даже если я добавлю префикс строки к u
, Какое преобразование или метод я могу вызвать, чтобы сделать len
вернуть правильное количество символов? Я попытался нормализовать и закодировать его с помощью следующего кода, но он по-прежнему сообщал ту же длину и возвращал тот же отступ.
unicodedata.normalize('NFKD', row.couponUuid).encode('ascii', 'ignore')
Попытка более простого трюка с кодировкой, предоставляемого @Ignacio, также не снижает его
str = row.couponUuid.encode('latin-1', 'replace')
print "Padding {0} with length {1}, mod 8 is {2}".format(s, len(s), len(s) % 8)
str = str.ljust(int(math.ceil(len(str) / 8.0) * 8), '=')
Либо с 'replace'
или же 'ingore'
, он все еще печатает: Padding ANEMTUTPUZFZFH6ANXNW5IOI4U with length 52, mod 8 is 4
Дополнительная информация по запросу @dano:
print repr(row.uuid)
показывает кодировку строки в юникоде:
u'A\x00N\x00E\x00M\x00T\x00U\x00T\x00P\x00U\x00Z\x00F\x00Z\x00F\x00H\x006\x00A\x00N\x00X\x00N\x00W\x005\x00I\x00O\x00I\x004\x00U\x00'
База данных, из которой он извлекается, - это Vertica (я думаю, в семействе 7.x). Я не уверен, что его набор символов, но тип столбца VARCHAR(26)
, Он извлекается из базы данных через соединение PyODBC. Я не специально кодирую или декодирую данные где-либо в моем коде. База данных Vertica заполнена другой кодовой базой, мне просто нужно вытащить ее с помощью Python.
Вот что Vertica может рассказать мне о столбце таблицы:
TABLE_CAT reporting
TABLE_SCHEM reporting_master
TABLE_NAME rmn_coupon
COLUMN_NAME uuid
DATA_TYPE 12
TYPE_NAME Varchar
COLUMN_SIZE 26
BUFFER_LENGTH 26
DECIMAL_DIGITS (null)
NUM_PREC_RADIX (null)
NULLABLE 1
REMARKS (null)
COLUMN_DEF
SQL_DATA_TYPE 12
SQL_DATETIME_SUB (null)
CHAR_OCTET_LENGTH 26
ORDINAL_POSITION 2
IS_NULLABLE YES
SCOPE_CATALOG (null)
SCOPE_SCHEMA (null)
SCOPE_TABLE (null)
SOURCE_DATA_TYPE (null)
1 ответ
Таким образом, использование очевидного подхода замены свободных нулевых байтов, кажется, делает свое дело. (вздох)
print repr(str)
str = str.replace('\x00', '')
print repr(str)
str = str.ljust(int(math.ceil(len(str) / 8.0) * 8), '=')
print repr(str)
Показывает этот вывод:
u'A\x00N\x00E\x00M\x00T\x00U\x00T\x00P\x00U\x00Z\x00F\x00Z\x00F\x00H\x006\x00A\x00N\x00X\x00N\x00W\x005\x00I\x00O\x00I\x004\x00U\x00'
u'ANEMTUTPUZFZFH6ANXNW5IOI4U'
u'ANEMTUTPUZFZFH6ANXNW5IOI4U======'
Где последняя строка является правильно дополненной строкой base32.
Этот вопрос всплыл в поиске Google для '\x00
python 'и дал мне подсказку.
Как отмечает Игнасио в комментариях выше, это также можно решить с помощью правильного кодирования и декодирования. Я не уверен, как вы можете определить правильную кодировку и кодировку, но UTF-16LE от Ignacio справляется с задачей.
str = str.encode('latin-1').decode('utf-16le')