Conversión base 62
¿Cómo convertirías un número entero a base 62 (como hexadecimal, pero con estos dígitos: '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ').
He estado tratando de encontrar una buena biblioteca de Python para ello, pero todas parecen estar ocupadas con la conversión de cadenas. El módulo Python base64 solo acepta cadenas y convierte un solo dígito en cuatro caracteres. Estaba buscando algo parecido a lo que usan los acortadores de URL.
No existe un módulo estándar para esto, pero he escrito mis propias funciones para lograrlo.
BASE62 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
def encode(num, alphabet):
"""Encode a positive number into Base X and return the string.
Arguments:
- `num`: The number to encode
- `alphabet`: The alphabet to use for encoding
"""
if num == 0:
return alphabet[0]
arr = []
arr_append = arr.append # Extract bound-method for faster access.
_divmod = divmod # Access to locals is faster.
base = len(alphabet)
while num:
num, rem = _divmod(num, base)
arr_append(alphabet[rem])
arr.reverse()
return ''.join(arr)
def decode(string, alphabet=BASE62):
"""Decode a Base X encoded string into the number
Arguments:
- `string`: The encoded string
- `alphabet`: The alphabet to use for decoding
"""
base = len(alphabet)
strlen = len(string)
num = 0
idx = 0
for char in string:
power = (strlen - (idx + 1))
num += alphabet.index(char) * (base ** power)
idx += 1
return num
Observe el hecho de que puede asignarle cualquier alfabeto para codificar y decodificar. Si omite el alphabet
argumento, obtendrá el alfabeto de 62 caracteres definido en la primera línea de código y, por lo tanto, codificará/decodificará a/desde 62 bases.
PD: para los acortadores de URL, he descubierto que es mejor omitir algunos caracteres confusos como 0Ol1oI, etc. Por eso uso este alfabeto para mis necesidades de acortamiento de URL."23456789abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"
Una vez escribí un guión para hacer esto también, creo que es bastante elegante :)
import string
# Remove the `_@` below for base62, now it has 64 characters
BASE_LIST = string.digits + string.letters + '_@'
BASE_DICT = dict((c, i) for i, c in enumerate(BASE_LIST))
def base_decode(string, reverse_base=BASE_DICT):
length = len(reverse_base)
ret = 0
for i, c in enumerate(string[::-1]):
ret += (length ** i) * reverse_base[c]
return ret
def base_encode(integer, base=BASE_LIST):
if integer == 0:
return base[0]
length = len(base)
ret = ''
while integer != 0:
ret = base[integer % length] + ret
integer /= length
return ret
Uso de ejemplo:
for i in range(100):
print i, base_decode(base_encode(i)), base_encode(i)