【问题标题】:How do I use a MD5 hash (or other binary data) as a key name?如何使用 MD5 哈希(或其他二进制数据)作为键名?
【发布时间】:2010-12-22 10:20:02
【问题描述】:

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

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

我不想使用hexdigest(),因为这不仅是一种杂物,而且也是一种劣质的东西(base64 会做得更好)。

【问题讨论】:

  • 您能解释一下为什么您认为使用十六进制摘要不是一个好主意吗?对我来说似乎是个好主意..
  • 我同意@Peter——我不清楚使用digest()hexdigest() 好多少;文档警告它可以包含非 ASCII 字符,包括 NULL 字节。在我看来,你在那里自找麻烦。你能解释一下为什么hexdigest() 在你的情况下是一个劣等的组合吗?
  • @Peter, @bgporter:我也同意。 hexdigest 所做的只是将摘要表示为表示摘要的十六进制数字的字符对。 "a" 的 MD5 摘要是 '\x0c\xc1u\xb9\xc0\xf1\xb6\xa81\xc3\x99\xe2iw&a',而十六进制摘要是 '0cc175b9c0f1b6a831c399e269772661'。如您所见,所有不能表示为 ascii 的字符(摘要中以 \x 开头)只是转换为十六进制摘要中的两个字符。是同一件事,表达方式不同。另外,这是您可以指定 AppEngine 密钥的唯一方法...我认为 vz0 的答案是正确的。

标签: python google-app-engine google-cloud-datastore


【解决方案1】:

App Engine Python docs 说:

key_name 存储为 Unicode 字符串(将 str 值转换为 ASCII 文本)。

密钥必须是 unicode-encodeable-string。您需要将 digest() 调用更改为 hexdigest(),即:

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

【讨论】:

    【解决方案2】:

    iso-8859-1解码字节串

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

    这基本上是一个“NOP”转换。它创建一个与初始字符串长度相同的 unicode 对象,如果您愿意,可以通过 .encode("iso-8859-1") 将其转换回字符串

    【讨论】:

    • @Noah:目前还不清楚为什么你更喜欢它而不是 hexdigest,因为它的数据量大约是 2 倍,而且计算速度肯定更慢。
    • @bukzor,unicode 对象的大小与摘要完全相同,它们与摘要的字节精确相同(请记住,'\xfc' 是单个字符等)刚刚标记为 unicode。 hexdigest 是更长的那个。它比仅仅使用 hexdigest 慢,但 appengine 需要稍后 .decode() str,所以总体而言使用 hexdigest 很可能会更慢。
    • 你说的很对。我想我仍然没有完全掌握这个 unicode 的东西。我没有仔细阅读以看到“u”,命令行会告诉我 len=16。我将其添加到我的“答案”中
    【解决方案3】:

    让我们考虑一下数据大小。这里的最优解是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-encodable 字节,因此我们必须做其他事情。简单的做法是使用 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
    

    【讨论】:

    • 不是直接的答案,但仍可能是相关信息 +1
    【解决方案4】:

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

    import hashlib
    import base64
    print base64.b64encode(hashlib.md5('thecakeisalie').digest())
    

    【讨论】:

    • 我没有投反对票,但一个明显的问题可能是 base64 编码可以生成同时包含 /+ 字符的字符串,这些字符可能会导致 URL 解析/转义/等。
    【解决方案5】:

    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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-09-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-28
      • 1970-01-01
      • 2011-06-06
      相关资源
      最近更新 更多