Преобразование базы 62

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

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

Ответ 1

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

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, вы получите 62-символьный алфавит, определенный в первой строке кода, и, следовательно, кодирование/декодирование на/из базы 62.

Надеюсь, что это поможет.

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

Удачи.

Ответ 2

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

import string
# Remove the '[email protected]' below for base62, now it has 64 characters
BASE_LIST = string.digits + string.letters + '[email protected]'
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)

Ответ 3

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

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)

Ответ 4

Если вы ищете максимальную эффективность (например, 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)

Ответ 5

Вероятно, вы хотите base64, а не base62. Там версия, совместимая с URL-адресом, плавает вокруг, поэтому лишние два символа-заполнителя не должны быть проблемой.

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

Ответ 7

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

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

Ответ 8

вы можете загрузить модуль zbase62 из pypi

eg

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

Ответ 9

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

Ответ 10

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

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-адресов.

Ответ 11

Здесь мое решение:

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 больше, чем N, и нарезает все a так, чтобы их последовательный меньше N, уменьшая a каждый раз, когда функция вызывается рекурсивно текущим aN*base**N.

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


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

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

Ответ 12

Если вы используете инфраструктуру django, вы можете использовать модуль django.utils.baseconv.

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

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

Ответ 13

Лично мне нравится решение от Байшампаяна, в основном из-за того, что вы сбиваете с толку символов.

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

Ответ 14

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

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

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

Ответ 15

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

Ответ 16

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

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

Ответ 17

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

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

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

Ответ 18

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

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

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

Вы можете сделать это самостоятельно (это псевдокод):

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

Ответ 19

с простой рекурсией

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

Ответ 20

Python 3.7.x

Я нашел PhD GitHub для некоторых алгоритмов при поиске существующего сценария base62. В настоящее время это не работает для текущей максимальной версии Python 3, поэтому я пошел дальше и исправил, где это было необходимо, и провел небольшой рефакторинг. Я обычно не работаю с Python и всегда использовал его специально, поэтому YMMV. Весь кредит идет на доктора Чжихуа Лай. Я только что проработал изгибы для этой версии Python.

файл base62.py

#modified from Dr. Zhihua Lai 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

файл 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])}')

вывод try_base62.py

Base10 ==> Base62
0 => 0
[...]
998 => g6
Base62 ==> Base10
gud => 63377
GA => 2640
mE => 1404
lo => 1326
lz => 1337
OMFGWTFLMFAOENCODING => 577002768656147353068189971419611424

Since there was no licensing info in the repo I did submit a PR so the original author at least knows other people are using and modifying their code.