2010-03-24 15 views
20

¿Cómo convertirías una cadena arbitraria en un entero único, que sería el mismo en todas las sesiones y plataformas de Python? Por ejemplo, hash('my string') no funcionaría porque se devuelve un valor diferente para cada sesión y plataforma de Python.Hashing persistente de cadenas en Python

Respuesta

8

Si una función de hash realmente no funciona para usted, puede convertir la cadena en un número.

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 

Esto es invertible, mediante la asignación de cada triplete a través de 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

Esto asigna '\ 0' y '\ 0 \ 0' a la misma cosa - debe anteponer un '1'. También esto es un poco ineficiente, podría usar la representación hexadecimal para que tenga números más pequeños (esto es equivalente a usar la representación binaria de la cadena e interpretarla como un número). – redtuna

30

utilizar un algoritmo de hash como MD5 o SHA1, a continuación, convertir el hexdigest través int():

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

Esta es una buena respuesta, pero técnicamente el entero producido no es única * *. Hay menos valores hash MD5 que cadenas disponibles. Sin embargo, las posibilidades de una colisión son muy bajas –

+3

Este es el caso de cualquier método hash. – MatthieuW

+0

¿Qué significa "muy bajo"? ¿No sería prudente utilizar este algoritmo en producción cuando se requiere exclusividad? – kalu

2

En primer lugar, es probable que no realmente quieren que los números enteros para ser realmente único. Si lo haces, tus números podrían ser de un tamaño ilimitado. Si eso es lo que realmente quiere, podría usar una biblioteca bignum e interpretar los bits de la cadena como la representación de un entero (potencialmente muy grande). Si sus cadenas pueden incluir el carácter \ 0, debe anteponer un 1, para que pueda distinguir, p. "\ 0 \ 0" desde "\ 0".

Ahora, si prefiere números de tamaño limitado, usará alguna forma de hash. MD5 funcionará, pero es excesivo para el propósito declarado. Recomiendo usar sdbm en su lugar, funciona muy bien. En C se ve así:

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 fuente, http://www.cse.yorku.ca/~oz/hash.html, también presenta algunas otras funciones hash.

+0

Estás en lo cierto. Esto definitivamente sería un problema si estuviera tratando de convertir documentos enteros a un número. Sin embargo, para mi aplicación, solo convertiré cadenas cortas, generalmente menos de unas pocas docenas de caracteres. – Cerin

3

Éstos son mi implementación de algoritmos python27 enumerados aquí: http://www.cse.yorku.ca/~oz/hash.html. No tengo idea de si son eficientes o no.

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

Aquí hay otra opción, bastante cruda (probablemente tiene muchas colisiones) y no muy legible.

Se trabajó con el propósito de generar un int (y más tarde, un color al azar) para diferentes cadenas:

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