Основание 62 преобразование
Как бы вы преобразовали целое число в основание 62 (например, шестнадцатеричное, но с этими цифрами: '0123456789abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz').
Я пытался найти хорошую библиотеку Python для него, но все они, кажется, заняты преобразованием строк. Модуль Python base64 принимает только строки и превращает одну цифру в четыре символа. Я искал что-то похожее на то, что используют сокращатели URL.
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), а не кодировать/декодировать что-то, этот модуль может помочь:
вы можете скачать модуль 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...Итак, цель состоит в том, чтобы найти всеas.для каждого
N=1,2,3...код изолируетaN*base**Nна "moduloing" поbнаb=base**(N+1), который нарезает всеas больше, чем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