Python中字符串的持久化哈希

38
您如何将任意字符串转换为唯一的整数,使其在Python会话和平台之间保持一致?例如hash('my string')不可行,因为每个Python会话和平台都会返回不同的值。

如果您能澄清您是否需要独特性的保证,还是满足于哈希函数的高概率独特性,那将会很有帮助。您提到hash()函数似乎暗示了后者...?您是否需要能够反转映射呢? - user1142217
5个回答

50

使用哈希算法,例如MD5或SHA1,然后通过int()hexdigest转换:

>>> import hashlib
>>> int(hashlib.md5('Hello, world!').hexdigest(), 16)
144653930895353261282233826065192032313L

7
这是一个不错的回答,但从技术上讲,产生的整数并不是唯一的。可用的字符串比MD5哈希少。然而,发生冲突的概率非常低。 - Eli Bendersky
8
任何哈希方法都是如此。 - MatthieuW
如果需要唯一性,那么不要使用哈希;而应该使用顺序编号或UUID。 - Ignacio Vazquez-Abrams
4
稍作修改:想要限制 int 的大小:int(hashlib.md5('Hello, world!').hexdigest()[:8], 16) 将会小于 2^32, int(hashlib.md5('Hello, world!').hexdigest()[:16], 16) 将会小于 2^64。 - Peter
3
如果你收到了 TypeError: Unicode-objects must be encoded before hashing 的错误提示,那么你需要对字符串进行编码,例如:int(hashlib.md5('Hello, world!'.encode('utf-8')).hexdigest(), 16) - Dylan Hogg
显示剩余2条评论

9
如果哈希函数真的无法满足您的需求,您可以将字符串转换为数字。
my_string = 'my string'
def string_to_int(s):
    ord3 = lambda x : '%.3d' % ord(x)
    return int(''.join(map(ord3, s)))

In[10]: string_to_int(my_string)
Out[11]: 109121032115116114105110103L

这是可逆的,通过将每个三元组映射到 chr
def int_to_string(n)
    s = str(n)
    return ''.join([chr(int(s[i:i+3])) for i in range(0, len(s), 3)])

In[12]: int_to_string(109121032115116114105110103L)
Out[13]: 'my string'

5
这个映射将 '\0' 和 '\0\0' 映射为同一件事情 - 你应该在前面添加 '1'。此外,这样做有点低效,可以使用十六进制表示法,这样数字会更小(这相当于使用字符串的二进制表示形式并将其解释为数字)。 - redtuna

3

这里是我对http://www.cse.yorku.ca/~oz/hash.html中列出的算法的Python27实现。

不确定它们是否高效。

from ctypes import c_ulong

def ulong(i): return c_ulong(i).value  # numpy would be better if available

def djb2(L):
  """
  h = 5381
  for c in L:
    h = ((h << 5) + h) + ord(c) # h * 33 + c
  return h
  """
  return reduce(lambda h,c: ord(c) + ((h << 5) + h), L, 5381)

def djb2_l(L):
  return reduce(lambda h,c: ulong(ord(c) + ((h << 5) + h)), L, 5381)

def sdbm(L):
  """
  h = 0
  for c in L:
    h = ord(c) + (h << 6) + (h << 16) - h
  return h
  """
  return reduce(lambda h,c: ord(c) + (h << 6) + (h << 16) - h, L, 0)

def sdbm_l(L):
  return reduce(lambda h,c: ulong(ord(c) + (h << 6) + (h << 16) - h), L, 0)

def loselose(L):
  """
  h = 0
  for c in L:
    h += ord(c);
    return h
  """
  return sum(ord(c) for c in L)

def loselose_l(L):
  return reduce(lambda h,c: ulong(ord(c) + h), L, 0)

2

首先,你可能并不真的想要整数实际上是唯一的。如果是这样的话,你的数字可能是无限大小的。如果确实是你想要的,那么你可以使用一个大整数库,并将字符串的位解释为(潜在非常大的)整数的表示形式。如果你的字符串可以包含\0字符,那么你应该在前面添加1,这样你就可以区分例如"\0\0"和"\0"。

现在,如果你喜欢有界大小的数字,你将使用某种形式的哈希。MD5可以工作,但对于所述目的来说有些过度。我建议使用sdbm,它非常有效。在C中,它看起来像这样:

static unsigned long sdbm(unsigned char *str)
{
    unsigned long hash = 0;
    int c;

    while (c = *str++)
        hash = c + (hash << 6) + (hash << 16) - hash;

    return hash;
}

这个源地址http://www.cse.yorku.ca/~oz/hash.html还提供了一些其他的哈希函数。


你说得很对。如果我试图将整个文档转换为数字,这肯定会是一个问题。但是,对于我的应用程序来说,我只需要转换短字符串,通常少于几十个字符。 - Cerin

0

这里还有另一个选项,相当粗糙(可能有很多冲突),并且不太可读。

它对于生成不同字符串的int(以及随机颜色)的目的起到了作用:

aString = "don't panic"
reduce( lambda x,y:x+y, map( lambda x:ord(x[0])*x[1],zip( aString, range( 1, len( aString ) ) ) ) )

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接