如何计算字符而不是字节?

时间:2014-08-22 01:40:50

标签: python-2.7 character-encoding pyodbc vertica base32

我有一些uuids存储在数据库中作为base32编码的字符串而没有填充。它们的长度为26个字符。我试图在Python 2.7.5中提取它们并将它们转换为不同数据存储的二进制数据。问题出现在我的Python DB实用程序将这些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

并且文档说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 个答案:

答案 0 :(得分: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字符串。

This question点击谷歌搜索&#39; \x00 python&#39;并给了我一些提示。

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

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