【问题标题】:How to count characters instead of bytes?如何计算字符而不是字节?
【发布时间】:2014-08-22 01:40:49
【问题描述】:

我有一些 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''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)

【问题讨论】:

  • print repr(row.uuid) 显示什么?你知道你的数据库使用什么编码来存储 uuid 吗?你是如何将uuid 拉出数据库的?您是否在某处明确对其进行解码?
  • 这是 UTF-16LE,解码不正确,即按字节而不是实际的 UTF-16LE。
  • @IgnacioVazquez-Abrams 听起来是对的。关于如何正确解码的任何提示? (或转换它?)由于框架,我不确定我是否可以访问连接工厂以传递特定的编码。
  • 将其编码为拉丁语 1 (.encode('latin-1')) 将撤消字节解码,以便您可以正确解码。
  • @IgnacioVazquez-Abrams 没有骰子。请参阅中间的编辑。

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


【解决方案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 在谷歌搜索“\x00 python”时弹出并给了我提示。

正如 Ignacio 在上面的 cmets 中指出的,这也可以通过使用正确的编码和解码来解决。我不确定您如何分辨正确的编码和编码,但 Ignacio 的 UTF-16LE 可以解决问题。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-02-13
    • 1970-01-01
    • 2018-04-19
    • 2017-08-28
    • 2019-05-09
    • 2019-07-02
    • 1970-01-01
    相关资源
    最近更新 更多