Conversion de la Base 62

comment convertir un entier en base 62 (comme hexadécimal, mais avec ces chiffres: '0123456789abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz').

j'ai essayé de trouver une bonne bibliothèque Python pour cela, mais ils semblent tous être occupés à convertir des cordes. Le module Python base64 n'accepte que les chaînes et transforme un seul chiffre en quatre caractères. Je cherchais quelque chose qui ressemble à ce que les raccourcisseurs D'URL utilisent.

66
demandé sur martineau 2009-07-13 18:19:41

18 réponses

Il n'y a pas de module standard pour cela, mais j'ai écrit mes propres fonctions pour y parvenir.

BASE62 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

def encode(num, alphabet=BASE62):
    """Encode a positive number in Base X

    Arguments:
    - `num`: The number to encode
    - `alphabet`: The alphabet to use for encoding
    """
    if num == 0:
        return alphabet[0]
    arr = []
    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 encoding
    """
    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

Notez le fait que vous pouvez lui donner n'importe quel alphabet pour encoder et décoder. Si vous quittez l'argument alphabet , vous obtiendrez l'alphabet de 62 caractères défini sur la première ligne de code, et donc encodage/décodage à/à partir de 62 base.

Espérons que cette aide.

PS - pour les raccourcisseurs D'URL, J'ai trouvé qu'il vaut mieux laisser de côté quelques personnages déroutants comme 0Ol1oI etc. Ainsi j'utilise cet alphabet pour mes besoins de raccourcissement D'URL - "23456789abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"

amusez-vous bien.

131
répondu Baishampayan Ghose 2016-06-16 19:28:07

j'ai écrit un script pour faire ça aussi, je pense que c'est assez élégant:)

import string
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

exemple d'usage:

for i in range(100):                                    
    print i, base_decode(base_encode(i)), base_encode(i)
40
répondu Wolph 2016-04-26 14:27:01

le décodeur suivant fonctionne avec n'importe quelle base raisonnable, a une boucle Beaucoup plus raide, et donne un message d'erreur explicite quand il rencontre un caractère invalide.

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)
8
répondu John Machin 2009-09-28 14:20:24

si vous êtes à la recherche de la plus haute efficacité (comme django), vous aurez besoin de quelque chose comme ce qui suit. Ce code est une combinaison de méthodes efficaces de Baishampayan Ghose et WoLpH et 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

Vous pouvez aussi calculer votre dictionnaire à l'avance. (Note: L'encodage avec une chaîne montre plus d'efficacité qu'avec une liste, même avec des nombres très longs.)

>>> timeit.timeit("for i in xrange(1000000): base.base_decode(base.base_encode(i))", setup="import base", number=1)
2.3302059173583984

codé et décodé 1 million de numéros en moins de 2,5 secondes. (2,2 Ghz i7-2670QM)

7
répondu Sepero 2013-04-24 07:56:02

vous voulez probablement la base64, pas la base62. Il y a une version compatible URL de celui-ci flottant autour, donc les deux caractères supplémentaires de remplissage ne devrait pas être un problème.

le processus est assez simple; considérez que base64 représente 6 bits et un octet régulier représente 8. Assignez une valeur de 000000 à 111111 à chacun des 64 caractères choisis, et mettez les 4 valeurs ensemble pour correspondre à un ensemble de 3 octets de base256. Répétez pour chaque jeu de 3 octets, padding à la fin avec votre choix du caractère de remplissage (0 est généralement utile).

4
répondu Williham Totland 2009-07-13 14:26:05

j'ai une bibliothèque Python pour faire exactement cela ici: http://www.djangosnippets.org/snippets/1431/

3
répondu Simon Willison 2009-09-28 10:59:20

si tout ce dont vous avez besoin est de générer un ID court (puisque vous mentionnez les raccourcis URL) plutôt que d'encoder/décoder quelque chose, ce module pourrait vous aider:

https://github.com/stochastic-technologies/shortuuid /

3
répondu Stavros Korokithakis 2011-01-08 14:59:07

vous pouvez télécharger le module zbase62 de pypi

eg

>>> import zbase62
>>> zbase62.b2a("abcd")
'1mZPsa'
2
répondu ghostdog74 2009-07-13 15:00:35

j'ai grandement bénéficié des autres postes ici. J'avais besoin du code python à l'origine pour un projet Django, mais depuis, je me suis tourné vers node.js, donc voici un version javascript du code (la partie encodage) que Baishampayan Ghose fourni.

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"));
2
répondu Stephen 2011-01-18 19:58:32

j'espère que l'extrait suivant pourrait aider.

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

Usage pour votre cas:

number = 367891
alphabet = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
print num2sym(number, alphabet)  # will print '1xHJ'

évidemment, vous pouvez spécifier un autre alphabet, consistant en un nombre inférieur ou supérieur de symboles, puis il convertira votre nombre à la base de nombre inférieur ou supérieur. Par exemple, en fournissant '01' comme alphabet la chaîne de production représentant le nombre d'entrée comme binaire.

vous pouvez mélanger l'alphabet initialement à ayez votre représentation unique des nombres. Il peut être utile si vous faites le service de raccourci D'URL.

2
répondu Vladimir Ignatyev 2013-06-25 16:52:15

voici ma solution:

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

explication

dans n'importe quelle base chaque nombre est égal à a1+a2*base**2+a3*base**3... donc le but est de trouver tous les a s.

pour chaque N=1,2,3... le code isole le aN*base**N par "moduloing" par b pour b=base**(N+1) qui coupe tous les a plus grands que N , et découpant tous les a de sorte que leurs serial est plus petit que N en diminuant a chaque fois que la fonction est appelée récursivement par le courant aN*base**N .

Base%(base-1)==1 donc base**p%(base-1)==1 et donc q*base^p%(base-1)==q avec une seule exception, lorsque q==base-1 qui renvoie 0 . Pour corriger ce cas, il retourne 0 . La fonction vérifie 0 depuis le début.


avantages

dans cet échantillon il n'y a qu'une seule multiplication (au lieu d'une division) et quelques opérations de module, qui sont toutes relativement rapides.

2
répondu Shu ba 2016-05-06 00:18:08

personnellement, J'aime la solution de Baishampayan, principalement à cause de la suppression des caractères confus.

pour plus d'exhaustivité, et une solution avec de meilleures performances, ce post montre une façon d'utiliser le module Python base64.

1
répondu Van Gale 2009-07-14 03:55:44

j'ai écrit ça il y a un moment et ça a plutôt bien marché (négatifs et tout compris)

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

désolé pour la longueur de tout

1
répondu Thropian 2011-08-29 00:12:28
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
1
répondu paulkav1 2013-03-29 00:50:19

Voici une façon récurive et itérative de le faire. L'itératif est un peu plus rapide selon le nombre d'exécution.

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
1
répondu wenzul 2014-10-15 15:13:04

il y a maintenant une bibliothèque python pour cela.

je travaille sur un paquet pip pour ça.

je vous recommande d'utiliser mon bases.py https://github.com/kamijoutouma/bases.py qui s'inspire des 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

se référer à https://github.com/kamijoutouma/bases.py#known-basesalphabets pour quelles bases sont utilisables

1
répondu Belldandu 2015-05-27 10:12:10

si vous utilisez django framework, vous pouvez utiliser django.utils.module baseconv.

>>> from django.utils import baseconv
>>> baseconv.base62.encode(1234567890)
1LY7VK

en plus de base62, baseconv a également défini base2/base16/base36/base56/base64.

1
répondu heronotears 2018-01-18 07:32:09

Désolé, je ne peux pas vous aider avec une bibliothèque ici. Je préférerais utiliser base64 et ajouter des caractères supplémentaires à votre choix -- si possible!

alors vous pouvez utiliser le module base64.

Si c'est vraiment, vraiment pas possible:

vous pouvez le faire vous-même de cette façon (c'est un pseudo-code):

base62vals = []
myBase = 62
while num > 0:
   reminder = num % myBase
   num = num / myBase
   base62vals.insert(0, reminder)
0
répondu Juergen 2009-07-13 14:26:58