代码之家  ›  专栏  ›  技术社区  ›  mikl

基62转换

  •  68
  • mikl  · 技术社区  · 15 年前

    您将如何将整数转换为以62为基的整数(如十六进制,但使用这些数字:“0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZABCEDEFGHIJKLMNOPQRSTUVWXYZ”)。

    我一直试图为它找到一个好的Python库,但它们似乎都在忙于转换字符串。python base64模块只接受字符串,并将单个数字转换为四个字符。我在寻找类似于URL缩短器使用的东西。

    17 回复  |  直到 6 年前
        1
  •  137
  •   martineau Nae    8 年前

    这方面没有标准的模块,但我已经编写了自己的函数来实现这一点。

    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
  •  42
  •   Blender    8 年前

    我曾经写过一个脚本来做这个,我认为它相当优雅:)

    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)
    
        3
  •  8
  •   John Machin Santi    15 年前

    下面的解码器生成器使用任何合理的基础,具有更整齐的循环,并在遇到无效字符时给出明确的错误消息。

    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
  •  7
  •   Sepero    11 年前

    如果你在寻找最高的效率(如Django),你会想要如下的东西。这是一个有效的方法结合白沙姆帕扬戈泽和沃尔夫和约翰马金。

    # 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
    

    在2.5秒内编码和解码了100万个数字。(2.2GHz i7-2670qm)

        5
  •  4
  •   Williham Totland    15 年前

    你可能想要base64,而不是base62。它有一个与URL兼容的版本,所以额外的两个填充字符不应该是问题。

    这个过程相当简单;考虑base64代表6位,常规字节代表8。为所选的64个字符中的每一个指定一个从000000到111111的值,并将4个值放在一起以匹配一组3位256字节。对每组3个字节重复此操作,在末尾填充您选择的填充字符(0通常很有用)。

        6
  •  3
  •   Stavros Korokithakis    13 年前

    如果您所需要的只是生成一个简短的ID(因为您提到了URL缩短器),而不是编码/解码某些内容,那么此模块可能会帮助:

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

        7
  •  2
  •   ghostdog74    15 年前

    您可以从下载zbase62模块 pypi

    >>> import zbase62
    >>> zbase62.b2a("abcd")
    '1mZPsa'
    
        8
  •  2
  •   Stephen    13 年前

    我从别人的帖子中获益匪浅。我最初需要在Django项目中使用python代码,但从那时起我就转向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"));
    
        9
  •  2
  •   Vladimir Ignatev    11 年前

    我希望下面的片段能有所帮助。

    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”作为字母表将输出表示输入数字为二进制的字符串。

    你可以先改变字母表,使其具有唯一的数字表示。如果你在做网址缩短服务,它会很有帮助。

        10
  •  2
  •   martineau Nae    8 年前

    我的解决方案是:

    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) 哪一片全切了 比S大 N 把所有的 所以他们的序列号小于 n 通过减少 每次函数被当前 A*BASE**N .

    Base%(base-1)==1 因此 base**p%(base-1)==1 因此 q*base^p%(base-1)==q 只有一个例外,当 q==base-1 哪些回报 0 . 要解决这个问题,它会返回 . 功能检查 从一开始。


    优势

    在这个示例中,只有一个乘法(而不是除法)和一些模运算,它们都比较快。

        11
  •  2
  •   Ryan Fau    6 年前

    如果使用django框架,则可以使用django.utils.baseconv模块。

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

    除了base62,baseconv还定义base2/base16/base36/base56/base64。

        12
  •  1
  •   Van Gale    15 年前

    就我个人而言,我喜欢白沙巴扬的解决方案,主要是因为去掉了令人困惑的角色。

    为了完整性和更好的性能解决方案, this post 显示了使用python base64模块的方法。

        13
  •  1
  •   martineau Nae    13 年前

    我以前写过这篇文章,效果很好(包括底片)

    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
    

    对不起,这一切的长度

        14
  •  1
  •   paulkav1    11 年前
    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
    
        15
  •  1
  •   wenzul    10 年前

    这是一种反复迭代的方法。根据执行的计数,迭代的速度要快一点。

    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
    
        16
  •  1
  •   Belldandu    9 年前

    现在有了一个Python库。

    我正在为这个做一个PIP包。

    我建议你用我的基地。 https://github.com/kamijoutouma/bases.py 灵感来源于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
    

    参照 https://github.com/kamijoutouma/bases.py#known-basesalphabets 什么样的基础是有用的

        17
  •  0
  •   Juergen    15 年前

    对不起,我帮不了你办图书馆。如果可能的话,我更喜欢使用base64,只需添加额外的字符就可以了!

    然后您可以使用base64模块。

    如果这真的,真的不可能:

    您可以自己这样做(这是伪代码):

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