Как преобразовать целое число в основание 62 (например, шестнадцатеричное, но с этими цифрами: '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ').
Я пытался найти для него хорошую библиотеку Python, но все они, похоже, заняты преобразованием строк. Модуль Python base64 принимает только строки и превращает одну цифру в четыре символа. Я искал что-то похожее на то, что используют сокращатели URL.
Ответы:
Для этого нет стандартного модуля, но я написал свои собственные функции для этого.
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
Обратите внимание на тот факт, что вы можете указать ему любой алфавит для кодирования и декодирования. Если вы опустите
alphabet
аргумент, вы получите 62-символьный алфавит, определенный в первой строке кода, и, следовательно, кодирование / декодирование в / из 62-й базы.Надеюсь это поможет.
PS - Для сокращателей URL-адресов я обнаружил, что лучше опустить несколько сбивающих с толку символов, таких как 0Ol1oI и т. Д. Поэтому я использую этот алфавит для своих нужд по сокращению URL-адресов -
"23456789abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"
Радоваться, веселиться.
источник
$-_.+!*'(),;/?:@&=
вы, вероятно, можете использовать и другие персонажи, например,[]~
и т. Д.Я как-то написал скрипт для этого тоже, думаю, довольно элегантно :)
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
Пример использования:
for i in range(100): print i, base_decode(base_encode(i)), base_encode(i)
источник
reversed(string)
быстрее, чем нарезкаstring[::-1]
в функции base_decode.integer /= length
на,integer //=length
чтобы получить правильный остатокСледующий производитель декодеров работает с любой разумной базой, имеет гораздо более аккуратный цикл и выдает явное сообщение об ошибке, когда встречает недопустимый символ.
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), вам понадобится что-то вроде следующего. Этот код представляет собой комбинацию эффективных методов от Байшампаяна Гхоша, WoLpH и Джона Мачина.
# 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)
источник
tuple()
вокругBASE_ALPH
в самом начале. В Python каждая строка повторяется. Эта функция, конечно же, используетсяenumerate()
. Так что код становится еще компактнее :)Если все, что вам нужно, это сгенерировать короткий идентификатор (поскольку вы упомянули сокращатели URL), а не кодировать / декодировать что-либо, этот модуль может помочь:
https://github.com/stochastic-technologies/shortuuid/
источник
Если вы используете фреймворк django, вы можете использовать модуль django.utils.baseconv.
>>> from django.utils import baseconv >>> baseconv.base62.encode(1234567890) 1LY7VK
В дополнение к base62, baseconv также определил base2 / base16 / base36 / base56 / base64.
источник
Вероятно, вам нужен base64, а не base62. Есть версия, совместимая с URL-адресами, поэтому дополнительные два символа-заполнителя не должны быть проблемой.
Процесс довольно прост; Учтите, что base64 представляет 6 бит, а обычный байт представляет 8. Присвойте значение от 000000 до 111111 каждому из 64 выбранных символов и сложите 4 значения вместе, чтобы соответствовать набору из 3 байтов base256. Повторите эти действия для каждого набора из 3 байтов, заполняя в конце символом заполнения по вашему выбору (обычно полезно 0).
источник
Для этого теперь есть библиотека Python.
Я работаю над созданием пакета pip для этого.
Я рекомендую вам использовать мой 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, чтобы узнать, какие базы можно использовать
источник
вы можете скачать модуль zbase62 из pypi
например
>>> import zbase62 >>> zbase62.b2a("abcd") '1mZPsa'
источник
Мне очень помогли публикации здесь других людей. Первоначально мне был нужен код Python для проекта Django, но с тех пор я обратился к node.js, поэтому вот версия кода javascript (часть кодирования), которую предоставил Байшампаян Гхош.
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-адресов.
источник
if num < 0 or type(num) not in (int, long):
.long
его нет в Py 3.x, поэтому можно использовать этот ответ .isinstance(x, (type(1), type(2**32)))
.Вот мое решение:
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
по модулю" на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
с самого начала.преимущества
В этом примере есть только одно умножение (вместо деления) и несколько операций модуля, которые все относительно быстрые.
источник
Лично мне нравится решение от Байшампаяна, в основном из-за удаления сбивающих с толку символов.
Для полноты и решения с лучшей производительностью в этом посте показан способ использования модуля 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
3.7.x
Я нашел github для некоторых алгоритмов при поиске существующего скрипта base62 . На данный момент это не сработало для текущей max-версии Python 3, поэтому я пошел дальше и исправил там, где это было необходимо, и провел небольшой рефакторинг. Я обычно не работаю с Python и всегда использовал его специально, поэтому YMMV. Вся заслуга доктора Чжихуа Лая . Я только что исправил недостатки этой версии Python.
файл
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
файл
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
Поскольку в репо не было информации о лицензировании, я отправил PR, чтобы первоначальный автор хотя бы знал, что другие люди используют и изменяют свой код.
источник
Извините, я не могу помочь вам с библиотекой здесь. Я бы предпочел использовать base64 и просто добавлять дополнительные символы по вашему выбору - если это возможно!
Затем вы можете использовать модуль base64.
Если это действительно невозможно:
Вы можете сделать это сами (это псевдокод):
base62vals = [] myBase = 62 while num > 0: reminder = num % myBase num = num / myBase base62vals.insert(0, reminder)
источник
с простой рекурсией
""" 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)
источник
Самый простой.
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"))
источник