Python:将字节与字符串连接

10

我正在开发一个Python项目,该项目支持Python 3,并且正在进行开发。具体来说,我正在开发一个摘要MD5算法。

在Python 2.6中,如果不运行以下导入:

from __future__ import unicode_literals

我能够编写如下代码:

a1 = hashlib.md5("%s:%s:%s" % (self.username, self.domain, self.password)).digest() 
a1 = "%s:%s:%s" %(a1, challenge["nonce"], cnonce )

没有任何问题,我的身份验证很好用。当我使用导入unicode_literals的相同代码行时,会出现异常:
UnicodeDecodeError: 'utf8' codec can't decode byte 0xa8 in position 0: unexpected code byte 由于我对Python相对较新,所以在解决这个问题时有些困惑。如果我将格式化字符串中的%s替换为%r,我就能够连接字符串,但是身份验证无法正常工作。我阅读过的digest-md5规范说,必须将16个八位二进制摘要附加到这些其他字符串后面。
你有什么想法吗?

1
Python 3.x明确区分字符串和字节数组。根据您的需求,将模式“%s:%s:%s”前置为b可能有效,但这可能会导致错误的结果。无论如何,这段代码的目的是什么? - Philipp
这是一段更大的代码片段,用于digest-md5算法,我正在使用它来对抗xmpp服务器进行身份验证,而这是导致我一些问题的具体代码。在格式化字符串之前添加b仍然会导致相同的问题。这里有关于创建digest-md5的更多信息:http://web.archive.org/web/20050224191820/http://cataclysm.cx/wip/digest-md5-crash.html - Macdiesel
2个回答

8
您所观察到的行为原因是,from __future__ import unicode_literals 改变了 Python 处理字符串的方式:
  • 在 2.x 系列中,没有 u 前缀的字符串被视为字节序列,每个字节可以在 \x00-\xff(包括)范围内。带有 u 前缀的字符串是 UCS-2 编码的 Unicode 序列。
  • 在 Python 3.x 中,以及在 unicode_literals 未来版本中,没有 u 前缀的字符串是使用 UCS-2 或 UCS-4 编码的 Unicode 字符串(取决于编译 Python 时使用的编译器标志)。带有 b 前缀的字符串是数据类型为 bytes 的文本,类似于 3.x 之前的非 Unicode 字符串。

无论使用哪个版本的 Python,都需要进行字节字符串和 Unicode 字符串之间的转换。默认情况下执行的转换取决于您系统的默认字符集;在您的情况下,这是 UTF-8。如果没有设置任何内容,则应该是 ascii,它拒绝所有高于 \x7f 的字符。

hashlib.md5(...).digest() 返回的消息摘要是一个字节串,我想您也希望整个操作的结果是一个字节串。如果您希望如此,请将 nonce 和 cnonce 字符串转换为字节字符串。

a1 = hashlib.md5("%s:%s:%s"  % (self.username, self.domain, self.password)).digest()
# note that UTF-8 may not be the encoding required by your counterpart, please check
a1 = b"%s:%s:%s" %(a1, challenge["nonce"].encode("UTF-8"), cnonce.encode("UTF-8") )

另外,您可以将从调用 digest() 返回的字节串转换为Unicode字符串(不推荐)。由于UCS-2的低8位等同于ISO-8859-1,因此这可能符合您的需求:

a1 = hashlib.md5("%s:%s:%s"  % (self.username, self.domain, self.password)).digest()
a1 = "%s:%s:%s" %(a1.decode("ISO-8859-1"), challenge["nonce"], cnonce)

第一个解决方案已经在代码中成功运行。非常感谢您的有见地的答案。 - Macdiesel

3
问题在于一旦导入了unicode_literals,"%s:%s:%s"就变成了一个unicode字符串。哈希输出是一个“常规”字符串。Python试图将常规字符串解码为Unicode字符串并失败(如预期的那样。哈希输出应该看起来像噪音)。请将您的代码更改为以下内容:
a1 = a1 + str(':') + str(challenge["nonce"]) + str(':') + str(cnonce)

我假设cnoncechallenge["nonce"]都是普通字符串。如果需要更多地控制它们被转换为字符串的过程,可以使用以下方法:

a1 += str(':') + challenge["nonce"].encode('UTF-8') + str(':') + cnonce.encode('UTF-8')

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接