0

我有一些 uuid 作为 base32 编码字符串存储在数据库中,没有填充。它们的长度为 26 个字符。我正在尝试在 Python 2.7.5 中提取它们并将它们转换为二进制数据以用于不同的数据存储。问题出现在我的 Python DB 实用程序将这些 base32 字符串解释为每个字符 2 个字节的 unicode 时。这是代码:

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'or '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)
4

1 回答 1

2

因此,采用替换备用空字节的明显方法似乎可以解决问题。(叹)

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 字符串。

这个问题\x00在谷歌搜索“ python”时弹出并给了我提示。

正如 Ignacio 在上面的评论中指出的那样,这也可以通过使用正确的编码和解码来解决。我不确定你怎么知道正确的编码和编码是什么,但 Ignacio 的 UTF-16LE 可以解决问题。

str = str.encode('latin-1').decode('utf-16le')
于 2014-08-22T16:23:50.873 回答