Как считать символы вместо байтов?

У меня есть некоторые 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')
Другие вопросы по тегам