Python:用字符串连接字节

时间:2010-07-01 12:02:40

标签: python string md5

我正在研究2.6中的python项目,该项目也有未来支持python 3的工作。具体来说,我正在研究digest-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'编解码器无法解码位置0的字节0xa8:意外的代码字节

现在我对python比较陌生,所以我有点陷入困境。如果我将格式化字符串中的%s替换为%r我能够连接字符串,但身份验证不起作用。我读过的digest-md5规范说明16字节二进制摘要必须附加到其他字符串上。

有什么想法吗?

2 个答案:

答案 0 :(得分:6)

您观察到的行为的原因是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-strings转换为byte-strings。:

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)

答案 1 :(得分:2)

问题是,导入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')