Python 如何使用MD5哈希(或其他二进制数据)作为密钥名?

Python 如何使用MD5哈希(或其他二进制数据)作为密钥名?,python,google-app-engine,google-cloud-datastore,Python,Google App Engine,Google Cloud Datastore,我一直试图在AppEngine上使用MD5哈希作为键名,但我编写的代码引发了UnicodeDecodeError from google.appengine.ext import db import hashlib key = db.Key.from_path('Post', hashlib.md5('thecakeisalie').digest()) 我不想使用hexdigest(),因为这不仅是一个乱七八糟的问题,也是一个低级问题(base64会做得更好)。我发现使用二进制数据的base6

我一直试图在AppEngine上使用MD5哈希作为键名,但我编写的代码引发了UnicodeDecodeError

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

我不想使用
hexdigest()
,因为这不仅是一个乱七八糟的问题,也是一个低级问题(base64会做得更好)。

我发现使用二进制数据的base64编码是一个合理的解决方案。根据您的代码,您可以执行以下操作:

import hashlib
import base64
print base64.b64encode(hashlib.md5('thecakeisalie').digest())
应用程序引擎显示:

密钥名称存储为Unicode 字符串(str值转换为 ASCII文本)

密钥必须是unicode可编码字符串。您需要将digest()调用更改为hexdigest(),即:


使用


这基本上是一个“NOP”转换。它创建了一个长度与初始字符串相同的unicode对象,只要通过
.encode(“iso-8859-1”)
即可将其转换回字符串。如果您愿意,让我们考虑一下数据大小。这里的最佳解决方案是16字节:

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

>>> len(hashlib.md5('thecakeisalie').digest())
16
>>> hashlib.md5('thecakeisalie').hexdigest() 
'27fcce8468a91e8a123ba5b14beaefd6'

>>> len(hashlib.md5('thecakeisalie').hexdigest())
32
您首先想到的是hexdigest,但它不是非常接近16字节:

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

>>> len(hashlib.md5('thecakeisalie').digest())
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,因为它可能是最优化的(最节省时间)


格尼布勒的答案长度为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

App Engine中的实体密钥可以具有ID(4字节整数)或名称(500字节UTF-8编码字符串)

MD5摘要是16字节的二进制数据:对于整数来说太大,(很可能)UTF-8无效。必须使用某种形式的编码

如果hexdigest()在32字节处过于冗长,请在24字节处尝试base64

无论您使用何种编码方案,最终都会被数据存储转换为UTF-8,因此下面的内容看起来是一种最佳编码

>>> u = hashlib.md5('thecakeisalie').digest().decode("iso-8859-1")
>>> len(u)
16
…编码到其最终表示形式时,比base64编码长两个字节:

>>> s = u.encode('utf-8')
>>> len(s)
26

你能解释一下为什么你不认为使用十六进制摘要是个好主意吗?对我来说似乎是个好主意……我同意w@Peter——我不清楚使用
digest()
在任何方面都比
hexdigest()好多少;文档警告它可能包含非ASCII字符,包括空字节。我觉得你是在自找麻烦。你能解释一下为什么在你的情况下,
hexdigest()
是个差劲的混球吗?@Peter,@bgporter:我也同意。hexdigest所做的只是将摘要表示为表示摘要的十六进制数字的字符对。
“a”
的MD5摘要是
“\x0c\xc1u\xb9\xc0\xf1\xb6\xa81\xc3\x99\xe2iw&a”
,而hexdigest是
“0cc175b9c0f1b6a831c399e269772661”
。如您所见,所有不能表示为ascii的字符(摘要中以
\x
开头)在摘要中仅转换为两个字符。这是同样的事情,用不同的方式表达。另外,这是唯一可以指定AppEngine的键的方法。。。我认为vz0的答案是正确的。我没有投反对票,但一个明显的问题可能是base64编码可以生成同时包含
/
+
字符的字符串,这些字符可能会导致URL解析/转义等出错。这不是直接的答案,但仍然有潜在的相关信息+1@Noah:不清楚为什么您更喜欢它而不是hexdigest,因为它的数据量大约是hexdigest的2倍,而且计算速度肯定较慢。@bukzor,unicode对象与digest的大小完全相同,它们与digest的字节完全相同(请记住“\xfc”是单个字符的repr等)只是标记为unicode。hexdigest是更长的一个。这比只做hexdigest要慢,但是appengine需要稍后对str进行
.decode()
处理,所以总体来说,使用hexdigest可能会慢一些。你说得很对。我想我还是不能完全掌握unicode的东西。我读得不够仔细,看不到“u”,命令行会告诉我len=16。我在我的“答案”中加上这个