我有一组ASCII字符串,比如文件路径。它们可能既短又很长。
我正在寻找一种算法,可以计算此类字符串的哈希值,而这个哈希值也将是一个字符串,但将具有固定长度,就像YouTube视频ID一样:
https://www.youtube.com/watch?v=-F-3E8pyjFo
^^^^^^^^^^^
我似乎需要使用MD5,但关键是我需要一个短的哈希字符串。
是否有一个能够实现这一点的shell命令或python库?
从 Python 3 开始,这种方法不再适用:
Python 内置了一个哈希函数 hash(),非常快速,适用于大多数情况:
>>> hash("dfds")
3591916071403198536
您可以将其变为无符号:
>>> hashu=lambda word: ctypes.c_uint64(hash(word)).value
您可以将其转换为一个16字节的十六进制字符串:
>>> hashu("dfds").to_bytes(8,"big").hex()
或者是一个 N*2 字节的字符串,其中 N <= 8:
>>> hashn=lambda word, N : (hashu(word)%(2**(N*8))).to_bytes(N,"big").hex()
...等等。如果您希望N大于8个字节,只需进行两次哈希。Python内置的速度要快得多,除非您需要安全性(而不仅仅是碰撞抵抗),否则永远不必使用hashlib。
>>> hashnbig=lambda word, N : ((hashu(word)+2**64*hashu(word+"2"))%(2**(N*8))).to_bytes(N,"big").hex()
最后,使用urlsafe base64编码比使用“hex”获得更好的字符串
>>> hashnbigu=lambda word, N : urlsafe_b64encode(((hashu(word)+2**64*hash(word+"2"))%(2**(N*8))).to_bytes(N,"big")).decode("utf8").rstrip("=")
>>> hashnbigu("foo",16)
'ZblnvrRqHwAy2lnvrR4HrA'
注意事项:
请注意在Python 3.3及以上版本中,此函数是随机化的,并且对某些用例无效。您可以使用PYTHONHASHSEED = 0来禁用此功能。
请参见https://github.com/flier/pyfasthash,其中提供了快速、稳定的哈希函数,同样适用于非加密应用程序且不会使 CPU 负载过高。
不要在真实代码中使用这种 lambda 样式...将其写出来!而且在代码中添加2 ** 32这样的东西,而不是将其作为常量,这是不好的做法。
对于较小的应用程序,8字节的冲突防护足够了...... 小于一百万条目,则冲突几率小于0.0000001%。 这是一个12字节的b64编码字符串。 但是对于较大的应用程序可能不够。
16个字节对于缓存中的UUID / OID已经足够了。
以字节输入方式生成300k个16字节哈希值的速度比较。
builtin: 0.188
md5: 0.359
fnvhash_c: 0.113
对于复杂的输入(例如3个整数的元组),您需要将其转换为字节才能使用非内置哈希函数,这会增加很多转换开销,使内置哈希函数更适用。
builtin: 0.197
md5: 0.603
fnvhash_c: 0.284
str
、bytes
和 datetime
对象的 __hash__()
值会与一个不可预测的随机值混合("salted");可以通过设置环境变量 PYTHONHASHSEED=0
来禁用随机化,从而允许一组 Python 进程共享哈希值。 - Rabash我想这个问题是不相关的,因为基于观点,但至少有一个提示,我知道FNV哈希,因为它被The Sims 3用来在不同的内容包之间根据名称查找资源。他们使用64位版本,所以我想它足以避免在相对较大的参考字符串集中发生冲突。哈希很容易实现,如果没有模块满足您(例如,pyfasthash就有它的实现)。
要从中获取一个短字符串,我建议您使用base64编码。例如,这是base64编码的64位哈希的大小:nsTYVQUag88=
(您可以摆脱填充=
)。
编辑:我最终也遇到了与您相同的问题,因此我实施了上述想法:https://gist.github.com/Cilyan/9424144
md5
模块以外,你是指什么?(已弃用,现在可以使用hashlib
代替) - Ricardo Cárdenes