¿Cómo convertiría 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 todos parecen estar ocupados 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.
Respuestas:
No hay 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 la base 62.Espero que esto ayude.
PD: para los acortadores de URL, he descubierto que es mejor omitir algunos caracteres confusos como 0Ol1oI, etc. Por lo tanto, utilizo este alfabeto para mis necesidades de acortamiento de URL.
"23456789abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"
Que te diviertas.
fuente
$-_.+!*'(),;/?:@&=
Probablemente puedas usar otros personajes como,[]~
etc.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)
fuente
reversed(string)
más rápido que cortarstring[::-1]
en la función base_decode.integer /= length
ainteger //=length
para obtener el resto correctoEl siguiente creador de decodificadores funciona con cualquier base razonable, tiene un bucle mucho más ordenado y da un mensaje de error explícito cuando encuentra un carácter no válido.
def base_n_decoder(alphabet): """Return a decoder for a base-n encoded string Argument: - `alphabet`: The alphabet used for encoding """ base = len(alphabet) char_value = dict(((c, v) for v, c in enumerate(alphabet))) def f(string): num = 0 try: for char in string: num = num * base + char_value[char] except KeyError: raise ValueError('Unexpected character %r' % char) return num return f if __name__ == "__main__": func = base_n_decoder('0123456789abcdef') for test in ('0', 'f', '2020', 'ffff', 'abqdef'): print test print func(test)
fuente
**
operador en el bucle.Si está buscando la mayor eficiencia (como django), querrá algo como lo siguiente. Este código es una combinación de métodos eficientes de Baishampayan Ghose y WoLpH y John Machin.
# Edit this list of characters as desired. BASE_ALPH = tuple("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz") BASE_DICT = dict((c, v) for v, c in enumerate(BASE_ALPH)) BASE_LEN = len(BASE_ALPH) def base_decode(string): num = 0 for char in string: num = num * BASE_LEN + BASE_DICT[char] return num def base_encode(num): if not num: return BASE_ALPH[0] encoding = "" while num: num, rem = divmod(num, BASE_LEN) encoding = BASE_ALPH[rem] + encoding return encoding
Es posible que también desee calcular su diccionario por adelantado. (Nota: la codificación con una cadena muestra más eficiencia que con una lista, incluso con números muy largos).
>>> timeit.timeit("for i in xrange(1000000): base.base_decode(base.base_encode(i))", setup="import base", number=1) 2.3302059173583984
Codificó y descodificó 1 millón de números en menos de 2,5 segundos. (2,2 Ghz i7-2670QM)
fuente
tuple()
alrededorBASE_ALPH
al principio. En Python, cada cadena es iterable. Por supuesto, esa característica es explotada porenumerate()
. Entonces el código se vuelve aún más ágil :)Si todo lo que necesita es generar una ID corta (ya que menciona acortadores de URL) en lugar de codificar / decodificar algo, este módulo podría ayudar:
https://github.com/stochastic-technologies/shortuuid/
fuente
Si usa el framework django, puede usar el módulo django.utils.baseconv.
>>> from django.utils import baseconv >>> baseconv.base62.encode(1234567890) 1LY7VK
Además de base62, baseconv también definió base2 / base16 / base36 / base56 / base64.
fuente
Probablemente quieras base64, no base62. Hay una versión compatible con URL flotando, por lo que los dos caracteres de relleno adicionales no deberían ser un problema.
El proceso es bastante simple; considere que base64 representa 6 bits y un byte regular representa 8. Asigne un valor de 000000 a 111111 a cada uno de los 64 caracteres elegidos y coloque los 4 valores juntos para que coincidan con un conjunto de 3 base256 bytes. Repita para cada conjunto de 3 bytes, rellenando al final con su elección de carácter de relleno (0 es generalmente útil).
fuente
Ahora hay una biblioteca de Python para esto.
Estoy trabajando en hacer un paquete de pip para esto.
Te recomiendo que uses mi bases.py https://github.com/kamijoutouma/bases.py que se inspiró en bases.js
from bases import Bases bases = Bases() bases.toBase16(200) // => 'c8' bases.toBase(200, 16) // => 'c8' bases.toBase62(99999) // => 'q0T' bases.toBase(200, 62) // => 'q0T' bases.toAlphabet(300, 'aAbBcC') // => 'Abba' bases.fromBase16('c8') // => 200 bases.fromBase('c8', 16) // => 200 bases.fromBase62('q0T') // => 99999 bases.fromBase('q0T', 62) // => 99999 bases.fromAlphabet('Abba', 'aAbBcC') // => 300
consulte https://github.com/kamijoutouma/bases.py#known-basesalphabets para saber qué bases son utilizables
fuente
puede descargar el módulo zbase62 de pypi
p.ej
>>> import zbase62 >>> zbase62.b2a("abcd") '1mZPsa'
fuente
Me he beneficiado enormemente de las publicaciones de otros aquí. Necesitaba el código de Python originalmente para un proyecto de Django, pero desde entonces he recurrido a node.js, así que aquí hay una versión javascript del código (la parte de codificación) que proporcionó Baishampayan Ghose.
var ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; function base62_encode(n, alpha) { var num = n || 0; var alphabet = alpha || ALPHABET; if (num == 0) return alphabet[0]; var arr = []; var base = alphabet.length; while(num) { rem = num % base; num = (num - rem)/base; arr.push(alphabet.substring(rem,rem+1)); } return arr.reverse().join(''); } console.log(base62_encode(2390687438976, "123456789ABCDEFGHIJKLMNPQRSTUVWXYZ"));
fuente
Espero que el siguiente fragmento pueda ayudar.
def num2sym(num, sym, join_symbol=''): if num == 0: return sym[0] if num < 0 or type(num) not in (int, long): raise ValueError('num must be positive integer') l = len(sym) # target number base r = [] div = num while div != 0: # base conversion div, mod = divmod(div, l) r.append(sym[mod]) return join_symbol.join([x for x in reversed(r)])
Uso para su caso:
number = 367891 alphabet = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' print num2sym(number, alphabet) # will print '1xHJ'
Obviamente, puede especificar otro alfabeto, que consiste en un número mayor o menor de símbolos, luego convertirá su número a la base numérica mayor o menor. Por ejemplo, al proporcionar '01' como un alfabeto, se generará una cadena que representa el número de entrada como binario.
Puede mezclar el alfabeto inicialmente para tener su representación única de los números. Puede ser útil si está creando un servicio de acortador de URL.
fuente
if num < 0 or type(num) not in (int, long):
.long
no existe en Py 3.x, por lo que es posible que desee usar esta respuesta .isinstance(x, (type(1), type(2**32)))
.Esta es mi solución:
def base62(a): baseit = (lambda a=a, b=62: (not a) and '0' or baseit(a-a%b, b*62) + '0123456789abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'[a%b%61 or -1*bool(a%b)]) return baseit()
explicación
En cualquier base, cada número es igual a
a1+a2*base**2+a3*base**3...
Entonces, el objetivo es encontrar todas lasa
s.Por cada
N=1,2,3...
código aísla elaN*base**N
de "moduloing" porb
parab=base**(N+1)
que corta todoa
es más grande queN
, y cortar toda laa
s por lo que su serie es menor queN
al disminuira
cada vez que la función se llama de forma recursiva por la corrienteaN*base**N
.Base%(base-1)==1
porbase**p%(base-1)==1
lo tanto y por lo tantoq*base^p%(base-1)==q
con una sola excepción, cuandoq==base-1
regresa0
. Para arreglar ese caso, vuelve0
. La función busca0
desde el principio.ventajas
En esta muestra, solo hay una multiplicación (en lugar de una división) y algunas operaciones de módulo, que son todas relativamente rápidas.
fuente
Personalmente, me gusta la solución de Baishampayan, principalmente porque quita los confusos personajes.
Para mayor integridad y solución con mejor rendimiento, esta publicación muestra una forma de usar el módulo Python base64.
fuente
Escribí esto hace un tiempo y funcionó bastante bien (negativos y todo incluido)
def code(number,base): try: int(number),int(base) except ValueError: raise ValueError('code(number,base): number and base must be in base10') else: number,base = int(number),int(base) if base < 2: base = 2 if base > 62: base = 62 numbers = [0,1,2,3,4,5,6,7,8,9,"a","b","c","d","e","f","g","h","i","j", "k","l","m","n","o","p","q","r","s","t","u","v","w","x","y", "z","A","B","C","D","E","F","G","H","I","J","K","L","M","N", "O","P","Q","R","S","T","U","V","W","X","Y","Z"] final = "" loc = 0 if number < 0: final = "-" number = abs(number) while base**loc <= number: loc = loc + 1 for x in range(loc-1,-1,-1): for y in range(base-1,-1,-1): if y*(base**x) <= number: final = "{}{}".format(final,numbers[y]) number = number - y*(base**x) break return final def decode(number,base): try: int(base) except ValueError: raise ValueError('decode(value,base): base must be in base10') else: base = int(base) number = str(number) if base < 2: base = 2 if base > 62: base = 62 numbers = ["0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f", "g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v", "w","x","y","z","A","B","C","D","E","F","G","H","I","J","K","L", "M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"] final = 0 if number.startswith("-"): neg = True number = list(number) del(number[0]) temp = number number = "" for x in temp: number = "{}{}".format(number,x) else: neg = False loc = len(number)-1 number = str(number) for x in number: if numbers.index(x) > base: raise ValueError('{} is out of base{} range'.format(x,str(base))) final = final+(numbers.index(x)*(base**loc)) loc = loc - 1 if neg: return -final else: return final
perdón por la duración de todo
fuente
BASE_LIST = tuple("23456789ABCDEFGHJKLMNOPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz") BASE_DICT = dict((c, v) for v, c in enumerate(BASE_LIST)) BASE_LEN = len(BASE_LIST) def nice_decode(str): num = 0 for char in str[::-1]: num = num * BASE_LEN + BASE_DICT[char] return num def nice_encode(num): if not num: return BASE_LIST[0] encoding = "" while num: num, rem = divmod(num, BASE_LEN) encoding += BASE_LIST[rem] return encoding
fuente
Aquí hay una forma repetitiva e iterativa de hacerlo. El iterativo es un poco más rápido dependiendo del conteo de ejecución.
def base62_encode_r(dec): s = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' return s[dec] if dec < 62 else base62_encode_r(dec / 62) + s[dec % 62] print base62_encode_r(2347878234) def base62_encode_i(dec): s = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' ret = '' while dec > 0: ret = s[dec % 62] + ret dec /= 62 return ret print base62_encode_i(2347878234) def base62_decode_r(b62): s = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' if len(b62) == 1: return s.index(b62) x = base62_decode_r(b62[:-1]) * 62 + s.index(b62[-1:]) % 62 return x print base62_decode_r("2yTsnM") def base62_decode_i(b62): s = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' ret = 0 for i in xrange(len(b62)-1,-1,-1): ret = ret + s.index(b62[i]) * (62**(len(b62)-i-1)) return ret print base62_decode_i("2yTsnM") if __name__ == '__main__': import timeit print(timeit.timeit(stmt="base62_encode_r(2347878234)", setup="from __main__ import base62_encode_r", number=100000)) print(timeit.timeit(stmt="base62_encode_i(2347878234)", setup="from __main__ import base62_encode_i", number=100000)) print(timeit.timeit(stmt="base62_decode_r('2yTsnM')", setup="from __main__ import base62_decode_r", number=100000)) print(timeit.timeit(stmt="base62_decode_i('2yTsnM')", setup="from __main__ import base62_decode_i", number=100000)) 0.270266867033 0.260915645986 0.344734796766 0.311662500262
fuente
Pitón
3.7.x
Encontré un github de doctorado para algunos algoritmos cuando buscaba un script base62 existente . No funcionó para la versión máxima actual de Python 3 en este momento, así que seguí adelante y lo arreglé donde era necesario e hice una pequeña refactorización. No suelo trabajar con Python y siempre lo he usado ad-hoc, así que YMMV. Todo el mérito es para el Dr. Zhihua Lai . Acabo de resolver los problemas para esta versión de Python.
archivo
base62.py
#modified from Dr. Zhihua Lai's original on GitHub from math import floor base = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; b = 62; def toBase10(b62: str) -> int: limit = len(b62) res = 0 for i in range(limit): res = b * res + base.find(b62[i]) return res def toBase62(b10: int) -> str: if b <= 0 or b > 62: return 0 r = b10 % b res = base[r]; q = floor(b10 / b) while q: r = q % b q = floor(q / b) res = base[int(r)] + res return res
archivo
try_base62.py
import base62 print("Base10 ==> Base62") for i in range(999): print(f'{i} => {base62.toBase62(i)}') base62_samples = ["gud", "GA", "mE", "lo", "lz", "OMFGWTFLMFAOENCODING"] print("Base62 ==> Base10") for i in range(len(base62_samples)): print(f'{base62_samples[i]} => {base62.toBase10(base62_samples[i])}')
salida de
try_base62.py
Como no había información de licencia en el repositorio, envié un PR para que el autor original al menos sepa que otras personas están usando y modificando su código.
fuente
Lo siento, no puedo ayudarte con una biblioteca aquí. Preferiría usar base64 y simplemente agregar caracteres adicionales a su elección, ¡si es posible!
Entonces puedes usar el módulo base64.
Si esto es realmente imposible:
Puede hacerlo usted mismo de esta manera (esto es un pseudocódigo):
base62vals = [] myBase = 62 while num > 0: reminder = num % myBase num = num / myBase base62vals.insert(0, reminder)
fuente
con recursividad simple
""" This module contains functions to transform a number to string and vice-versa """ BASE = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" LEN_BASE = len(BASE) def encode(num): """ This function encodes the given number into alpha numeric string """ if num < LEN_BASE: return BASE[num] return BASE[num % LEN_BASE] + encode(num//LEN_BASE) def decode_recursive(string, index): """ recursive util function for decode """ if not string or index >= len(string): return 0 return (BASE.index(string[index]) * LEN_BASE ** index) + decode_recursive(string, index + 1) def decode(string): """ This function decodes given string to number """ return decode_recursive(string, 0)
fuente
El más simple de todos.
BASE62 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" def encode_base62(num): s = "" while num>0: num,r = divmod(num,62) s = BASE62[r]+s return s def decode_base62(num): x,s = 1,0 for i in range(len(num)-1,-1,-1): s = int(BASE62.index(num[i])) *x + s x*=62 return s print(encode_base62(123)) print(decode_base62("1Z"))
fuente