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

У меня есть некоторые uuids, хранящиеся в базе данных в виде строк с кодировкой base32 без заполнения. Длина их 26 символов. Я пытаюсь извлечь их в Python 2.7.5 и преобразовать их в двоичные данные для другого хранилища данных. Проблема возникает из-за того, что моя утилита DB Python интерпретирует эти строки base32 как unicode с 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
</type>

И документы говорят, что 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) показывает кодировку Unicode строки:

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 ' \x00 python' и дал мне подсказку.

Как отмечает Игнасио в комментариях выше, это также можно решить, используя правильное кодирование и декодирование. Я не уверен, как вы можете сказать, что такое правильное кодирование и кодировка, но Ignacio UTF-16LE делает трюк.

str = str.encode('latin-1').decode('utf-16le')

licensed under cc by-sa 3.0 with attribution.