Основание 62 преобразование



Как бы вы преобразовали целое число в основание 62 (например, шестнадцатеричное, но с этими цифрами: '0123456789abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz').



Я пытался найти хорошую библиотеку Python для него, но все они, кажется, заняты преобразованием строк. Модуль Python base64 принимает только строки и превращает одну цифру в четыре символа. Я искал что-то похожее на то, что используют сокращатели URL.

592   18  

18 ответов:

для этого нет стандартного модуля, но я написал свои собственные функции для достижения этого.

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

обратите внимание на то, что вы можете дать ему любое алфавит использовать для кодирования и декодирования. Если вы оставите alphabet аргумент out, вы получите 62-символьный алфавит, определенный в первой строке кода, и, следовательно, кодирование/декодирование в/из 62 базы.

надеюсь, что это помогает.

PS - Для сокращения URL-адреса, я обнаружил, что это лучше оставить несколько запутанных символов, таких как 0Ol1oI и т. д. Таким образом, я использую этот алфавит для моих потребностей сокращения URL - "23456789abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"

получать удовольствие.

Я когда-то написал сценарий, чтобы сделать это также, я думаю, что это довольно элегантно :)

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

пример использования:

for i in range(100):                                    
    print i, base_decode(base_encode(i)), base_encode(i)

следующий создатель декодера работает с любой разумной базой, имеет гораздо более аккуратный цикл и дает явное сообщение об ошибке, когда он встречает недопустимый символ.

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)

Если вы ищете самую высокую эффективность (например, django), вам понадобится что-то вроде следующего. Этот код представляет собой комбинацию эффективных методов от Baishampayan Ghose и WoLpH и 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

Вы можете также рассчитать свой словарь заранее. (Примечание: кодирование со строкой показывает большую эффективность, чем со списком, даже с очень длинными числами.)

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

закодировано и декодировано 1 миллион чисел менее чем за 2,5 секунды. (2.2 ГГц i7-2670QM)

вы, вероятно, хотите base64, а не base62. Существует его версия, совместимая с URL, поэтому дополнительные два символа наполнителя не должны быть проблемой.

процесс довольно прост; считайте, что base64 представляет 6 бит,а обычный байт представляет 8. Присвойте значение от 000000 до 111111 каждому из 64 выбранных символов и соедините 4 значения вместе, чтобы они соответствовали набору из 3 байтов base256. Повторите для каждого набора из 3 байт, заполнение в конце с вашим выбором знак-заполнитель (0-это вообще полезно).

У меня есть библиотека Python для выполнения именно этого здесь:http://www.djangosnippets.org/snippets/1431/

Если все, что вам нужно, это создать короткий идентификатор (так как вы упоминаете url shorteners), а не кодировать/декодировать что-то, этот модуль может помочь:

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

вы можете скачать модуль zbase62 из pypi

например

>>> import zbase62
>>> zbase62.b2a("abcd")
'1mZPsa'

Я получил большую пользу от сообщений других здесь. Мне нужен был код python изначально для проекта Django, но с тех пор я обратился к node.js, так вот a версия javascript кода (кодирующая часть), который предоставил 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"));

Я надеюсь, что следующий фрагмент кода мог бы помочь.

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

использование для вашего случая:

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

очевидно, вы можете указать другой алфавит, состоящий из меньшего или большего количества символов, тогда он преобразует ваше число в меньшую или большую базу чисел. Например, предоставление' 01 ' в качестве алфавита выведет строку, представляющую входное число как двоичное.

вы можете перетасовать алфавит изначально, чтобы иметь свое уникальное представление числа. Это может быть полезно, если вы делаете службу сокращения URL.

вот мое решение:

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

объяснение

в любой базе каждое число равно a1+a2*base**2+a3*base**3... Итак, цель состоит в том, чтобы найти все a s.

для каждого N=1,2,3... код изолирует aN*base**N на "moduloing" по b на b=base**(N+1), который нарезает все a s больше, чем N, и нарезать все as так, что их серийное меньше, чем N уменьшается a каждый раз, когда функция вызывается рекурсивно с помощью ток aN*base**N.

base**p%(base-1)==1 и поэтому q*base^p%(base-1)==q только с одним исключением, когда q==base-1 возвращает 0. Чтобы исправить этот случай, он возвращает 0. Функция проверяет наличие 0 С самого начала.


преимущества

в этом примере есть только одно умножение (вместо деления) и некоторые операции по модулю, которые все относительно быстро.

лично мне нравится решение от Baishampayan, в основном из-за зачистки запутанных персонажей.

для полноты и решения с лучшей производительностью,этот пост показывает способ использования модуля Python base64.

я писал это некоторое время назад и он работал очень хорошо (негативы и все включено)

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

извините за длину всего этого

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

вот рекурсивный и итеративный способ сделать это. Итерационный немного быстрее, в зависимости от количества выполнения.

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

для этого теперь есть библиотека python.

Я работаю над созданием пакета pip для этого.

Я рекомендую вам использовать мой bases.py https://github.com/kamijoutouma/bases.py который был вдохновлен основами.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

см.https://github.com/kamijoutouma/bases.py#known-basesalphabets для каких баз можно использовать

Если вы используете Django framework, вы можете использовать django.utils.модуль baseconv.

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

в дополнение к base62, baseconv также определил base2/base16/base36/base56 / base64.

Извините, я не могу помочь вам с библиотекой здесь. Я бы предпочел использовать base64 и просто добавлять дополнительные символы на ваш выбор - если это возможно!

затем вы можете использовать модуль base64.

Если это действительно невозможно:

вы можете сделать это сами таким образом (это псевдо-код):

base62vals = []
myBase = 62
while num > 0:
   reminder = num % myBase
   num = num / myBase
   base62vals.insert(0, reminder)

Comments

    Ничего не найдено.