2010-03-24 5 views
20

Comment convertir une chaîne arbitraire en un entier unique, ce qui serait le même pour les sessions et plates-formes Python? Par exemple hash('my string') ne fonctionnerait pas car une valeur différente est renvoyée pour chaque session et plate-forme Python.Hashing persistant de chaînes en Python

Répondre

8

Si une fonction de hachage ne fonctionne vraiment pas pour vous, vous pouvez transformer la chaîne en un nombre.

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 

Ceci est inversible, en mappant chaque triplet par 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

Ceci met en correspondance '\ 0' et '\ 0 \ 0' avec la même chose - vous devez ajouter un '1'. De plus, c'est un peu inefficace, vous pouvez utiliser la représentation hexadécimale pour avoir des nombres plus petits (cela équivaut alors à utiliser la représentation binaire de la chaîne et à l'interpréter comme un nombre). – redtuna

30

utiliser un algorithme de hachage telle que MD5 ou SHA1, puis convertir le hexdigest via int():

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

C'est une bonne réponse, mais techniquement l'entier produit n'est pas * * unique. Il y a moins de hachages MD5 que de chaînes disponibles. Cependant, les chances d'une collision sont très faibles –

+3

C'est le cas pour toute méthode de hachage. – MatthieuW

+0

Que signifie "très faible"? Serait-il imprudent d'utiliser cet algorithme en production lorsque l'unicité est requise? – kalu

2

Tout d'abord, vous ne probablement pas vraiment veulent les entiers pour être réellement unique. Si vous le faites, vos numéros pourraient être illimités. Si c'est vraiment ce que vous voulez, vous pouvez utiliser une bibliothèque bignum et interpréter les bits de la chaîne comme la représentation d'un entier (potentiellement très grand). Si vos chaînes peuvent inclure le caractère \ 0, vous devez ajouter un 1, ce qui vous permet de distinguer, par exemple, "\ 0 \ 0" à partir de "\ 0". Maintenant, si vous préférez des nombres de taille bornée, vous utiliserez une forme de hachage. MD5 fonctionnera mais c'est exagéré pour l'objectif annoncé. Je recommande d'utiliser sdbm à la place, cela fonctionne très bien. En C, il ressemble à ceci:

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; 
} 

La source, http://www.cse.yorku.ca/~oz/hash.html, présente également quelques autres fonctions de hachage.

+0

Vous avez tout à fait raison. Ce serait certainement un problème si j'essayais de convertir des documents entiers en nombre. Cependant, pour mon application, je ne convertirai que des chaînes courtes, généralement moins de quelques dizaines de caractères. – Cerin

3

Voici mon implémentation de python27 pour les algorithmes listés ici: http://www.cse.yorku.ca/~oz/hash.html. Aucune idée si elles sont efficaces ou non.

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) 
0

Voici une autre option, assez grossière (qui a probablement beaucoup de collisions) et qui n'est pas très lisible.

Il a travaillé dans le but de générer un int (et plus tard, une couleur aléatoire) pour les chaînes différentes:

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