Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/354.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在Python中将整数转换为最短的url安全字符串?_Python_Url_Base64 - Fatal编程技术网

如何在Python中将整数转换为最短的url安全字符串?

如何在Python中将整数转换为最短的url安全字符串?,python,url,base64,Python,Url,Base64,我想用最短的方式来表示URL中的整数。例如,可以使用十六进制将11234缩短为“2be2”。由于base64使用的是64个字符的编码,因此在base64中使用比十六进制更少的字符表示整数应该是可能的。问题是,我无法找到使用Python将整数转换为base64(然后再转换)的最干净方法 base64模块有处理ByTestring的方法——因此,一个解决方案可能是将整数转换为Python字符串的二进制表示形式。。。但我也不知道该怎么做。我会使用您建议的“将整数编码为二进制字符串,然后使用base64

我想用最短的方式来表示URL中的整数。例如,可以使用十六进制将11234缩短为“2be2”。由于base64使用的是64个字符的编码,因此在base64中使用比十六进制更少的字符表示整数应该是可能的。问题是,我无法找到使用Python将整数转换为base64(然后再转换)的最干净方法


base64模块有处理ByTestring的方法——因此,一个解决方案可能是将整数转换为Python字符串的二进制表示形式。。。但我也不知道该怎么做。

我会使用您建议的“将整数编码为二进制字符串,然后使用base64编码”方法,并使用struct:

>>> import struct, base64
>>> base64.b64encode(struct.pack('l', 47))
'LwAAAA=='
>>> struct.unpack('l', base64.b64decode(_))
(47,)
再次编辑: 要在太小而不需要完全32位精度的数字上去掉额外的0,请尝试以下操作:

def pad(str, l=4):
    while len(str) < l:
        str = '\x00' + str
    return str

>>> base64.b64encode(struct.pack('!l', 47).replace('\x00', ''))
'Lw=='
>>> struct.unpack('!l', pad(base64.b64decode('Lw==')))
(47,)
def垫(str,l=4):
而len(str)>>base64.b64encode(结构包('!l',47).替换('\x00','')
'Lw=='
>>>解包(“!l”,pad(base64.b64解码('Lw='))
(47,)

Base64需要4个字节/字符来编码3个字节,并且只能编码3个字节的倍数(否则会添加填充)


因此,在Base64中表示4个字节(您的平均整数)需要8个字节。用十六进制编码相同的4个字节也需要8个字节。因此,对于单个int,您不会获得任何结果。

如果您正在寻找使用base64缩短整数表示的方法,我认为您需要寻找其他方法。当你用base64编码时,它不会变短,事实上它会变长

例如,使用base64编码的11234将产生MTEyMzQ=


在使用base64时,您忽略了一个事实,即您没有将数字(0-9)转换为64字符编码。您正在将3个字节转换为4个字节,因此可以保证base64编码字符串的长度将延长33.33%。

如果您不希望使用base64编码,则需要在数字基数X中表示以10为基数的数字

如果您希望以26个可用字母表示以10为基数的数字,您可以使用:。 (通过使用所有合法url字符,您可以将该示例扩展到更大的基础)


您应该至少能够获得基数38(26个字母,10个数字,+,389;)

简单的一点是将字节字符串转换为web安全的base64:

import base64
output = base64.urlsafe_b64encode(s)
棘手的一点是第一步-将整数转换为字节字符串

如果整数很小,最好使用十六进制编码-参见

否则(黑客递归版本):


这个答案在精神上与道格拉斯·利德的答案相似,但有以下变化:

  • 它不使用实际的Base64,因此没有填充字符
  • 它没有首先将数字转换为字节字符串(以256为基数),而是直接将其转换为以64为基数,这样做的优点是可以使用符号字符表示负数

    import string
    ALPHABET = string.ascii_uppercase + string.ascii_lowercase + \
               string.digits + '-_'
    ALPHABET_REVERSE = dict((c, i) for (i, c) in enumerate(ALPHABET))
    BASE = len(ALPHABET)
    SIGN_CHARACTER = '$'
    
    def num_encode(n):
        if n < 0:
            return SIGN_CHARACTER + num_encode(-n)
        s = []
        while True:
            n, r = divmod(n, BASE)
            s.append(ALPHABET[r])
            if n == 0: break
        return ''.join(reversed(s))
    
    def num_decode(s):
        if s[0] == SIGN_CHARACTER:
            return -num_decode(s[1:])
        n = 0
        for c in s:
            n = n * BASE + ALPHABET_REVERSE[c]
        return n
    

    一些旁注:

    • 通过将string.digits放在字母表的第一位(并使用符号字符'-'),您可以(稍微)提高base-64数字的可读性;我选择了基于Python的urlsafe_b64encode的顺序
    • 如果要对大量负数进行编码,可以使用符号位或1/2的补码代替符号字符来提高效率
    • 通过更改字母表,您应该能够轻松地使此代码适应不同的基础,或者将其限制为仅使用字母数字字符,或者添加其他“URL安全”字符
    • 我建议不要在uri中使用base 10以外的表示形式,在大多数情况下,它会增加复杂性,并使调试变得更加困难,但与HTTP的开销相比,它不会带来显著的节约,除非您使用的是TinyURL式的表示形式

      • 有点老套,但它可以工作:

        def b64num(num_to_encode):
          h = hex(num_to_encode)[2:]     # hex(n) returns 0xhh, strip off the 0x
          h = len(h) & 1 and '0'+h or h  # if odd number of digits, prepend '0' which hex codec requires
          return h.decode('hex').encode('base64') 
        

        您可以将对.encode('base64')的调用替换为base64模块中的某些内容,例如urlsafe_b64encode()

        编码
        n

        data = ''
        while n > 0:
            data = chr(n & 255) + data
            n = n >> 8
        encoded = base64.urlsafe_b64encode(data).rstrip('=')
        
        要解码
        s

        data = base64.urlsafe_b64decode(s + '===')
        decoded = 0
        while len(data) > 0:
            decoded = (decoded << 8) | ord(data[0])
            data = data[1:]
        
        以及解码:

        decoded = 0
        while len(s) > 0:
            decoded = decoded * len(alphabet) + alphabet.find(s[0])
            s = s[1:]
        

        对此,您可能不希望使用实数base64编码—它将添加填充等,甚至可能导致字符串比小数字的十六进制字符串更大。如果不需要与其他任何东西进行互操作,只需使用您自己的编码。这里有一个函数,它将编码到任何基(注意,数字实际上是先存储最低有效位的,以避免额外的reverse()调用:

        这样做的好处是,只需添加适当的 将字符添加到编码器的基本字符串


        请注意,较大基数的增益不会太大。但是,基数64只会将基数16的大小减少到2/3(6位/字符,而不是4位)。每次加倍只会为每个字符增加一位。除非您确实需要压缩内容,否则仅使用十六进制可能是最简单和最快的选择。

        我维护了一个名为zbase62的小库:

        使用它,您可以将Python 2 str对象转换为base-62编码字符串,反之亦然:

        Python 2.7.1+ (r271:86832, Apr 11 2011, 18:13:53) 
        [GCC 4.5.2] on linux2
        Type "help", "copyright", "credits" or "license" for more information.
        >>> import os
        >>> d = os.urandom(32)
        >>> d
        'C$\x8f\xf9\x92NV\x97\x13H\xc7F\x0c\x0f\x8d9}\xf5.u\xeeOr\xc2V\x92f\x1b=:\xc3\xbc'
        >>> from zbase62 import zbase62
        >>> encoded = zbase62.b2a(d)
        >>> encoded
        'Fv8kTvGhIrJvqQ2oTojUGlaVIxFE1b6BCLpH8JfYNRs'
        >>> zbase62.a2b(encoded)
        'C$\x8f\xf9\x92NV\x97\x13H\xc7F\x0c\x0f\x8d9}\xf5.u\xeeOr\xc2V\x92f\x1b=:\xc3\xbc'
        
        但是,您仍然需要将integer转换为str。这是Python 3内置的:

        Python 3.2 (r32:88445, Mar 25 2011, 19:56:22)
        [GCC 4.5.2] on linux2
        Type "help", "copyright", "credits" or "license" for more information.
        >>> import os
        >>> d = os.urandom(32)
        >>> d
        b'\xe4\x0b\x94|\xb6o\x08\xe9oR\x1f\xaa\xa8\xe8qS3\x86\x82\t\x15\xf2"\x1dL%?\xda\xcc3\xe3\xba'
        >>> int.from_bytes(d, 'big')
        103147789615402524662804907510279354159900773934860106838120923694590497907642
        >>> x= _ 
        >>> x.to_bytes(32, 'big')
        b'\xe4\x0b\x94|\xb6o\x08\xe9oR\x1f\xaa\xa8\xe8qS3\x86\x82\t\x15\xf2"\x1dL%?\xda\xcc3\xe3\xba'
        

        在Python 2中,要将int转换为bytes,反之亦然,据我所知,没有一种方便的标准方法。我想也许我应该复制一些实现,例如:为方便起见,将其复制到zbase62中。

        我需要一个带符号的整数,所以我最终选择了:

        import struct, base64
        
        def b64encode_integer(i):
           return base64.urlsafe_b64encode(struct.pack('i', i)).rstrip('=\n')
        
        例如:

        >>> b64encode_integer(1)
        'AQAAAA'
        >>> b64encode_integer(-1)
        '_____w'
        >>> b64encode_integer(256)
        'AAEAAA'
        

        关于Base64给出的所有答案都是非常合理的解决方案。但它们在技术上是不正确的。要将整数转换为尽可能短的URL安全字符串,您需要的是Base66(有)

        该代码如下所示:

        from io import StringIO
        import urllib
        
        BASE66_ALPHABET = u"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_.~"
        BASE = len(BASE66_ALPHABET)
        
        def hexahexacontadecimal_encode_int(n):
            if n == 0:
                return BASE66_ALPHABET[0].encode('ascii')
        
            r = StringIO()
            while n:
                n, t = divmod(n, BASE)
                r.write(BASE66_ALPHABET[t])
            return r.getvalue().encode('ascii')[::-1]
        
        下面是这样一个方案的完整实现,可以作为pip可安装软件包使用:


        我正在为此制作一个pip包

        我建议你用我的ba
        Python 3.2 (r32:88445, Mar 25 2011, 19:56:22)
        [GCC 4.5.2] on linux2
        Type "help", "copyright", "credits" or "license" for more information.
        >>> import os
        >>> d = os.urandom(32)
        >>> d
        b'\xe4\x0b\x94|\xb6o\x08\xe9oR\x1f\xaa\xa8\xe8qS3\x86\x82\t\x15\xf2"\x1dL%?\xda\xcc3\xe3\xba'
        >>> int.from_bytes(d, 'big')
        103147789615402524662804907510279354159900773934860106838120923694590497907642
        >>> x= _ 
        >>> x.to_bytes(32, 'big')
        b'\xe4\x0b\x94|\xb6o\x08\xe9oR\x1f\xaa\xa8\xe8qS3\x86\x82\t\x15\xf2"\x1dL%?\xda\xcc3\xe3\xba'
        
        import struct, base64
        
        def b64encode_integer(i):
           return base64.urlsafe_b64encode(struct.pack('i', i)).rstrip('=\n')
        
        >>> b64encode_integer(1)
        'AQAAAA'
        >>> b64encode_integer(-1)
        '_____w'
        >>> b64encode_integer(256)
        'AAEAAA'
        
        from io import StringIO
        import urllib
        
        BASE66_ALPHABET = u"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_.~"
        BASE = len(BASE66_ALPHABET)
        
        def hexahexacontadecimal_encode_int(n):
            if n == 0:
                return BASE66_ALPHABET[0].encode('ascii')
        
            r = StringIO()
            while n:
                n, t = divmod(n, BASE)
                r.write(BASE66_ALPHABET[t])
            return r.getvalue().encode('ascii')[::-1]
        
        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
        
        def tetrasexagesimal(number):
            out=""
            while number>=0:
                if number == 0:
                    out = 'A' + out
                    break
                digit = number % 64
                out = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[digit] + out
                number /= 64 # //= 64 for py3 (thank spanishgum!)
                if number == 0:
                    break
            return out
        
        tetrasexagesimal(1)