如何将MD5哈希(或其他二进制数据)用作键名?

5 投票
5 回答
6306 浏览
提问于 2025-04-16 08:53

我一直在尝试在AppEngine上使用MD5哈希作为键名,但我写的代码出现了Unicode解码错误。

from google.appengine.ext import db
import hashlib
key = db.Key.from_path('Post', hashlib.md5('thecakeisalie').digest())

我不想使用hexdigest(),因为这不仅是个临时解决办法,而且效果也不好(用base64会更好)。

5 个回答

4

我们来聊聊数据大小的问题。这里的最佳解决方案是16个字节:

>>> hashlib.md5('thecakeisalie').digest() 
"'\xfc\xce\x84h\xa9\x1e\x8a\x12;\xa5\xb1K\xea\xef\xd6"

>>> len(hashlib.md5('thecakeisalie').digest())
16

你首先想到的是hexdigest,但它的大小离16个字节还差得远:

>>> hashlib.md5('thecakeisalie').hexdigest() 
'27fcce8468a91e8a123ba5b14beaefd6'

>>> len(hashlib.md5('thecakeisalie').hexdigest())
32

不过这样得到的结果不能直接转换成ascii编码的字节,所以我们得想别的办法。一个简单的方法是使用python的表示方式:

>>> repr(hashlib.md5('thecakeisalie').digest())
'"\'\\xfc\\xce\\x84h\\xa9\\x1e\\x8a\\x12;\\xa5\\xb1K\\xea\\xef\\xd6"'

>>> len(repr(hashlib.md5('thecakeisalie').digest()))
54

我们可以通过去掉那些"\x"的转义字符和周围的引号来简化很多内容:

>>> repr(hashlib.md5('thecakeisalie').digest())[1:-1].replace('\\x','')
"'fcce84ha91e8a12;a5b1Keaefd6"

>>> len(repr(hashlib.md5('thecakeisalie').digest())[1:-1].replace('\\x',''))
28

这样看起来不错,但base64的效果更好一些:

>>> base64.b64encode(hashlib.md5('thecakeisalie').digest())
J/zOhGipHooSO6WxS+rv1g==
>>> len(base64.b64encode(hashlib.md5('thecakeisalie').digest()))
24

总体来说,base64在空间利用上最有效,但我觉得用hexdigest会更好,因为它可能在时间上更优化。


Gnibbler的回答给出的长度是16个字节!

>>> hashlib.md5('thecakeisalie').digest().decode("iso-8859-1")
u"'\xfc\xce\x84h\xa9\x1e\x8a\x12;\xa5\xb1K\xea\xef\xd6"
>>> len(hashlib.md5('thecakeisalie').digest().decode("iso-8859-1"))
16
12

App Engine的Python文档中提到:

一个key_name是以Unicode字符串的形式存储的(而且str类型的值会被转换成ASCII文本)。

这个key必须是可以用Unicode编码的字符串。你需要把digest()这个调用改成hexdigest(),也就是说:

k = hashlib.md5('thecakeisalie').hexdigest()
5

iso-8859-1 来解码字节串。

>>> hashlib.md5('thecakeisalie').digest().decode("iso-8859-1")
u"'\xfc\xce\x84h\xa9\x1e\x8a\x12;\xa5\xb1K\xea\xef\xd6"

这基本上就是一个“无操作”的转换。它会生成一个 Unicode 对象,这个对象的长度和最开始的字符串是一样的。如果你想的话,可以通过 .encode("iso-8859-1") 轻松地把它转换回字符串。

撰写回答