Konwersja podstawy 62

92

Jak przekonwertować liczbę całkowitą na podstawę 62 (np. Szesnastkową, ale z tymi cyframi: „0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ”).

Próbowałem znaleźć do tego dobrą bibliotekę Pythona, ale wszyscy wydają się być zajęci konwersją ciągów. Moduł Python base64 akceptuje tylko ciągi znaków i zamienia pojedynczą cyfrę na cztery znaki. Szukałem czegoś podobnego do tego, czego używają skracacze adresów URL.

mikl
źródło
Brzmi jak ktoś po prostu znaleźć pomysł projekt open source :) Daj mi znać, jeśli znajdziesz coś albo zdecydować się na stworzenie własnego ...
samoz
Jeśli chcesz tworzyć krótkie adresy URL, możesz użyć całego zestawu znaków, które nie muszą być kodowane: en.wikipedia.org/wiki/Percent-encoding#Types_of_URI_characters . To 66 znaków.
l0b0
Myślę, że pominę kropkę i tyldę, żeby uniknąć dezorientacji użytkownika, ale myślnik i podkreślenia powinny być wartościowe, dziękuję.
mikl
a co z Base64? Możesz mieć więcej szczęścia w znalezieniu bibliotek do tego.
Mike Cooper,
Na to pytanie można znaleźć kilka odpowiedzi: stackoverflow.com/questions/561486/…
Miles

Odpowiedzi:

169

Nie ma do tego standardowego modułu, ale napisałem własne funkcje, aby to osiągnąć.

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

Zwróć uwagę, że możesz nadać mu dowolny alfabet, który będzie używany do kodowania i dekodowania. Jeśli opuściszalphabet argument, otrzymasz 62-znakowy alfabet zdefiniowany w pierwszej linii kodu, a tym samym kodowanie / dekodowanie do / z bazy 62.

Mam nadzieję że to pomoże.

PS - W przypadku skracaczy adresów URL odkryłem, że lepiej jest pominąć kilka mylących znaków, takich jak 0Ol1oI itp. Dlatego używam tego alfabetu do moich potrzeb w zakresie skracania adresów URL - "23456789abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"

Baw się dobrze.

Baishampayan Ghose
źródło
5
+1: Fajnie! Można to rozszerzyć o więcej znaków przyjaznych dla adresów URL, aby ewentualnie zapisać jeden znak tu i tam. Postacie, które znam są bezpieczne są: $-_.+!*'(),;/?:@&= Prawdopodobnie można korzystać z niektórych innych znaków zbyt podobny []~itp
Blixt
24
Błąd nazewnictwa: nie jest to podstawa 62, ponieważ alfabet można dostosować.
odpocząć
3
W przypadku dekodowania lepszym nawykiem jest nie obliczanie mocy (oszczędność czasu, krótszy zapis, ale co ważniejsze, pozwala uniknąć błędów typu off-by-one), stąd: num = 0; dla znaku w ciągu: num = num * base + alphabet.index (char)
ShreevatsaR
1
@ShreevatsaR: czy jest jakiś szczególny powód używania str.index () zamiast wyszukiwania w słowniku? Zobacz moją odpowiedź ...
John Machin
2
Jonathan - Python może obsługiwać numery dowolnej długości - nie ma przepełnienia: >>> 256 * (62 ** 100) 44402652562862911414971048359760030835982580330786570771137804709455598239929932673552190201125730101070867075377228748911717860448985185350731601887476350502973424822800696272224256L
Anthony Briggs
53

Kiedyś napisałem scenariusz, aby to zrobić, myślę, że jest dość elegancki :)

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

Przykładowe użycie:

for i in range(100):                                    
    print i, base_decode(base_encode(i)), base_encode(i)
Wolph
źródło
9
Ta wersja jest znacznie szybsza niż akceptowane rozwiązanie od Baishampayan. Zoptymalizowałem dalej, obliczając długość poza funkcją. Wyniki testów (100 000 iteracji): wersja-WoLpH: .403 .399 .399 .398 .398 | wersja Baishampayan: 1.783 1.785 1.782 1.788 1.784. Ta wersja jest około 4x szybsza.
Jordan,
jeśli używasz reversed(string)szybciej niż cięcie string[::-1]w funkcji base_decode.
ENDOH takanao
1
Znalezienie tego pytania zajęło mi dużo czasu. Nigdy nie wiedziałem, że nazywa się to konwersją base62. Niezła odpowiedź.
1
Musiałem zmienić integer /= lengthna, integer //=lengthaby uzyskać poprawną resztę
karlgold
10

Poniższy program do dekodowania działa z dowolną rozsądną podstawą, ma znacznie uporządkowaną pętlę i wyświetla wyraźny komunikat o błędzie, gdy napotka nieprawidłowy znak.

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)
John Machin
źródło
Chociaż prawdopodobnie nigdy bym tego nie użył, musiałem też dać ci kciuki za kreatywność. Ten kod mnie rozbawił. :)
Sepero
@Sepero: Co jest takie zabawne? To poważne, solidne oprogramowanie przemysłowe. Brak cofania Micky-Mouse z **operatorem w pętli.
John Machin,
Uspokój się przyjacielu. Masz rację. Brakowało mi prawdziwej dobroci twojej wewnętrznej pętli, ponieważ jest ona zakopana w rzeczach niezwiązanych z pytaniem (zawijanie, sprawdzanie błędów, testowanie jednostkowe).
Sepero
Wygląda dobrze, ale czy nie zapomniałeś o enkoderze "przemysłowym", który pobiera liczbę całkowitą i alfabet, aby utworzyć łańcuch?
martineau
1
Czy q w ostatniej wartości było zamierzone, aby pokazać wywoływany błąd ValueError?
Thomas Vander Stichele
8

Jeśli szukasz najwyższej wydajności (jak django), będziesz potrzebować czegoś takiego jak poniżej. Ten kod jest połączeniem wydajnych metod od Baishampayan Ghose oraz WoLpH i 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

Możesz również z wyprzedzeniem obliczyć słownik. (Uwaga: kodowanie ciągiem znaków wykazuje większą wydajność niż lista, nawet przy bardzo długich liczbach).

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

Zakodowanie i zdekodowanie 1 miliona liczb w mniej niż 2,5 sekundy. (2,2 Ghz i7-2670QM)

Sepero
źródło
Na początku niekoniecznie trzeba mieć wszystko tuple()dookoła BASE_ALPH. W Pythonie każdy łańcuch jest iterowalny. Ta funkcja jest oczywiście wykorzystywana przez enumerate(). Kod staje się jeszcze szczuplejszy :)
Luis Nell
7
Hej, origiNell, masz rację, że krotka () nie jest potrzebna, ale w moim systemie sprawia, że ​​kod działa około 20% szybciej. Spróbuj przetestować to bez krotki () i zobacz, co działa najlepiej. Pozdrawiam :)
Sepero
1
Ciekawy punkt. Ma to sens, ponieważ krotki są lżejsze niż struny. Dzięki za oświecenie :)!
Luis Nell
@Sepero Jeszcze bardziej ulepszyłem twoją wersję pod względem formatowania, nazewnictwa, testów i funkcjonalności (obsługiwane są liczby ujemne): pastebin.com/4uket7iu (możesz zaktualizować swoją odpowiedź)
Joschua
@Joschua - Twój kod pod Twoim adresem URL nie działa dla mnie. base_encode () wydawał się generować tylko jedną zakodowaną cyfrę dla liczb, które testowałem.
SMGreenfield
4

Jeśli wszystko, czego potrzebujesz, to wygenerowanie krótkiego identyfikatora (ponieważ wspominasz o skracaczach adresów URL), a nie kodowanie / dekodowanie czegoś, ten moduł może pomóc:

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

Stavros Korokithakis
źródło
Nie jestem pewien, czy jest to odpowiednie dla krótkich adresów URL. UUID to zwykle bardzo duża liczba, więc nawet kodowanie base57 tak, jak on, jest dość długie w przypadku krótkiego adresu URL.
mikl
Możesz po prostu wyciąć tyle, ile chcesz, kolizje nadal będą mało prawdopodobne, ponieważ jest to czysto losowe, ale nie będzie już unikalnym identyfikatorem.
Stavros Korokithakis
4

Jeśli używasz frameworka django, możesz użyć modułu django.utils.baseconv.

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

Oprócz base62, baseconv zdefiniował również base2 / base16 / base36 / base56 / base64.

Ryan Fau
źródło
3

Prawdopodobnie chcesz base64, a nie base62. Istnieje jego wersja zgodna z adresem URL, więc dodatkowe dwa znaki wypełniające nie powinny stanowić problemu.

Proces jest dość prosty; weź pod uwagę, że base64 reprezentuje 6 bitów, a zwykły bajt reprezentuje 8. Przypisz wartość od 000000 do 111111 każdemu z 64 wybranych znaków i połącz 4 wartości, aby dopasować zestaw 3 bajtów base256. Powtórz to dla każdego zestawu 3 bajtów, dopełniając na końcu wybranym znakiem dopełniającym (zazwyczaj przydatne jest 0).

Williham Totland
źródło
5
Standardowe metody kodowania Python base64 nie są odpowiednie dla krótkich adresów URL, ponieważ są zoptymalizowane pod kątem kodowania bajtów (tj. Łańcuchów / liter) i dadzą dłuższe dane wyjściowe niż tylko podstawowa zmiana wartości liczbowej.
mikl
@mikl Oczywiście moduł base64 Pythona może nie nadawać się do generowania krótkich adresów URL, ale wszystkie metody kodowania Pythona naprawdę działają na sekwencjach liczb o podstawie 256. bajty są w rzeczywistości „łańcuchami” zakodowanymi w standardzie Base-256. Python 2.x traktuje łańcuchy jako sekwencję bajtów, podczas gdy Python 3.x (który robi właściwą rzecz) traktuje łańcuchy jako Unicode. Więc b'foobar 'jest naprawdę tylko fantazyjnym sposobem pisania [102, 111, 111, 98, 97, 114] lub [0x66,0x6f, 0x6f, 0x62,0x61,0x72] lub b' \ x66 \ x6f \ x6f \ x62 \ x61 \ x72 ', co nie jest zaskoczeniem, że jest reprezentacją base-256. Bajty nie są łańcuchami ani literami. Bajty to bajty. =)
takudeep
@yesudeep: A więc bajty to bajty… o co dokładnie chodzi?
martineau
3

Jest teraz do tego biblioteka Pythona.

Pracuję nad stworzeniem dla tego pakietu pip.

Polecam skorzystanie z mojego bases.py https://github.com/kamijoutouma/bases.py, który został zainspirowany 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

zobacz https://github.com/kamijoutouma/bases.py#known-basesalphabets, aby dowiedzieć się, jakie bazy są użyteczne

Belldandu
źródło
2

można pobrać moduł zbase62 z PyPI

na przykład

>>> import zbase62
>>> zbase62.b2a("abcd")
'1mZPsa'
ghostdog74
źródło
2
Tak, patrzyłem na to wcześniej, ale konwertuje łańcuchy, a nie liczby :)
mikl
2

Bardzo skorzystałem z postów innych osób. Początkowo potrzebowałem kodu Pythona do projektu Django, ale od tego czasu przeszedłem do node.js, więc oto wersja kodu javascript (część kodująca), którą dostarczył 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"));
Stephen
źródło
Zaktualizowałem ten kod i przekształciłem go w projekt open source dla każdego, kto jest zainteresowany github.com/sbussard/encode-the-things
Stephen
2

Mam nadzieję, że poniższy fragment może pomóc.

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

Użycie w twoim przypadku:

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

Oczywiście możesz określić inny alfabet, składający się z mniejszej lub większej liczby symboli, a następnie zamieni twoją liczbę na mniejszą lub większą podstawę liczb. Na przykład podanie „01” jako alfabetu spowoduje wyświetlenie ciągu reprezentującego liczbę wejściową w postaci binarnej.

Możesz początkowo potasować alfabet, aby uzyskać unikalną reprezentację liczb. Może to być pomocne, jeśli tworzysz usługę skracania adresów URL.

Vladimir Ignatyev
źródło
1
Nie jest zły. Możesz chcieć użyć if num < 0 or type(num) not in (int, long):.
martineau
Tak jest lepiej, ale jest to trochę bardziej skomplikowane, ponieważ longnie istnieje w Py 3.x - więc warto użyć tej odpowiedzi .
martineau
1
Lub użyć własnego przenośną wersję: isinstance(x, (type(1), type(2**32))).
martineau
2

Oto moje rozwiązanie:

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

wyjaśnienie

W każdej bazie każda liczba jest równa a1+a2*base**2+a3*base**3...Więc celem jest znalezienie wszystkicha s.

Dla każdego N=1,2,3...kodu izoluje aN*base**Nprzez „moduloing” przez bdla b=base**(N+1)którego wszystko plastry as większy niż Ni krojenie wszystko ajest tak, że ich seryjny jest mniejszy niż Npoprzez zmniejszenie akażdym razem funkcja jest wywoływana rekurencyjnie przez prąd aN*base**N.

Base%(base-1)==1dlatego base**p%(base-1)==1i dlatego q*base^p%(base-1)==qtylko z jednym wyjątkiem, kiedy q==base-1powraca 0. Aby naprawić ten przypadek, zwraca 0. Funkcja sprawdza 0od początku.


Zalety

W tym przykładzie jest tylko jedno mnożenie (zamiast dzielenia) i kilka operacji na modułach, które są stosunkowo szybkie.

Shu ba
źródło
1

Osobiście podoba mi się rozwiązanie Baishampayana, głównie ze względu na pozbycie się zagmatwanych postaci.

Aby uzyskać kompletność i rozwiązanie o lepszej wydajności, ten post pokazuje sposób korzystania z modułu Python base64.

Van Gale
źródło
1
Jak wspomniałem w moim komentarzu do Willihama Totlanda, base64 w Pythonie jest nieoptymalny do kodowania liczb, ponieważ jest zoptymalizowany pod kątem łańcuchów.
mikl
1

Napisałem to jakiś czas temu i działa całkiem nieźle (negatywy i wszystko w zestawie)

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

przepraszam za długość tego wszystkiego

Thropian
źródło
1
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
paulkav1
źródło
1
To naprawia nazwę BASE_LIST, a także odwraca ciąg przy dekodowaniu, który został pominięty w znakomitej odpowiedzi Spero
paulkav1
1

Oto rekurencyjny i iteracyjny sposób na zrobienie tego. Iteracyjny jest nieco szybszy w zależności od liczby wykonań.

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
wenzul
źródło
Naprawdę podobało mi się twoje podejście rekurencyjne. Moja córka, która brała udział w programie AP Comp Sci, wymyśliła to samo rozwiązanie dla mnie, aby zaimplementować „base25” (używając 'ABCDEFHJKMNPQRTUVWXY34789') w C ++. Poszedłem przekonwertować go do Pythona i będąc całkowitym nowicjuszem w tym języku napotkałem kilka przeszkód - które elegancko rozwiązałeś w jednej linii kodu! Unikniesz nawet częstego problemu z tłumaczeniem 0 na pusty ciąg w alfabetach, które nie zaczynają się od 0-9. Świetna robota! (Nie potrzebuję liczb ujemnych, ale twoje podejście było tak dobre, że fajnie byłoby to dodać dla przyszłych przeglądarek)
SMGreenfield
1

Pyton 3.7.x

Kiedy szukałem istniejącego skryptu base62, znalazłem github doktorancki dla niektórych algorytmów . W tym czasie nie działało to w obecnej wersji max Pythona 3, więc poszedłem do przodu i naprawiłem tam, gdzie było to potrzebne, i zrobiłem małą refaktoryzację. Zwykle nie pracuję z Pythonem i zawsze używałem go ad-hoc, więc YMMV. Wszystko zasługa dr Zhihua Lai . Właśnie rozwiązałem problemy związane z tą wersją Pythona.

plik 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

plik 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])}')

wyjście try_base62.py

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

Ponieważ w repozytorium nie było informacji o licencji, przesłałem PR, więc oryginalny autor przynajmniej wie, że inne osoby używają i modyfikują swój kod.

kayleeFrye_onDeck
źródło
0

Przepraszamy, nie mogę Ci pomóc z biblioteką tutaj. Wolałbym używać base64 i po prostu dodawać do wyboru dodatkowe znaki - jeśli to możliwe!

Następnie możesz użyć modułu base64.

Jeśli to naprawdę, naprawdę niemożliwe:

Możesz to zrobić samemu w ten sposób (to jest pseudokod):

base62vals = []
myBase = 62
while num > 0:
   reminder = num % myBase
   num = num / myBase
   base62vals.insert(0, reminder)
Juergen
źródło
0

z prostą rekurencją

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

Lokesh Sanapalli
źródło
0

Najprostsze w historii.

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"))
melvil james
źródło