我想要一种在URL中表示整数的最短方式.例如,11234可以使用十六进制缩短为"2be2".由于base64使用的是64字符编码,因此应该可以使用比十六进制更少的字符来表示base64中的整数.问题是我无法弄清楚使用Python将整数转换为base64(以及再返回)的最简洁方法.
base64模块有处理字节串的方法 - 所以也许一个解决方案是将一个整数转换为二进制表示形式作为Python字符串...但我不知道如何做到这一点.
这个答案与Douglas Leeder的精神相似,但有以下变化:
它不使用实际的Base64,因此没有填充字符
它不是首先将数字转换为字节串(基数256),而是将其直接转换为base 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
>>> num_encode(0) 'A' >>> num_encode(64) 'BA' >>> num_encode(-(64**5-1)) '$_____'
一些附注:
您可以(略微)通过将string.digits放在字母表中(并使符号字符' - ')来增加base-64数字的人类可读性; 我根据Python的urlsafe_b64encode选择了我的订单.
如果你编码了很多负数,你可以通过使用符号位或一个/两个补码而不是符号字符来提高效率.
您应该能够通过更改字母表轻松地将此代码调整到不同的基础,或者将其限制为仅包含字母数字字符或添加其他"URL安全"字符.
在大多数情况下,我建议不要在URI中使用基本10以外的表示 - 它增加了复杂性,并且与HTTP的开销相比,调试更加困难而且没有显着的节省 - 除非你想要TinyURL-esque.
关于Base64的所有答案都是非常合理的解决方案.但它们在技术上是不正确的.要将整数转换为可能的最短URL安全字符串,您需要的是base 66(有66个URL安全字符).
该代码如下所示:
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可安装包:
https://github.com/aljungberg/hexahexacontadecimal
你可能不需要真正的base64编码 - 它会添加填充等,甚至可能导致比小数字更大的字符串.如果不需要与其他任何东西进行互操作,只需使用您自己的编码即可.例如.这是一个将编码到任何基数的函数(注意数字实际上是存储最不重要的,以避免额外的reverse()调用:
def make_encoder(baseString): size = len(baseString) d = dict((ch, i) for (i, ch) in enumerate(baseString)) # Map from char -> value if len(d) != size: raise Exception("Duplicate characters in encoding string") def encode(x): if x==0: return baseString[0] # Only needed if don't want '' for 0 l=[] while x>0: l.append(baseString[x % size]) x //= size return ''.join(l) def decode(s): return sum(d[ch] * size**i for (i,ch) in enumerate(s)) return encode, decode # Base 64 version: encode,decode = make_encoder("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/") assert decode(encode(435346456456)) == 435346456456
这样做的好处是,只需向编码器的基本字符串添加适当的字符,即可使用所需的任何基础.
请注意,较大基数的收益不会那么大.base 64只会将大小减小到base 16的2/3rds(6位/ char而不是4位).每次加倍只会为每个字符增加一位.除非你真的需要压缩东西,否则只使用十六进制可能是最简单和最快的选择.
编码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:]
与其他一些"最佳"编码一样,您可以根据RFC 1738 使用73个字符(如果您将"+"视为可用,则实际为74个):
alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_`\"!$'()*,-." encoded = '' while n > 0: n, r = divmod(n, len(alphabet)) encoded = alphabet[r] + encoded
和解码:
decoded = 0 while len(s) > 0: decoded = decoded * len(alphabet) + alphabet.find(s[0]) s = s[1:]
简单的一点是将字节字符串转换为Web安全的base64:
import base64 output = base64.urlsafe_b64encode(s)
棘手的位是第一步 - 将整数转换为字节串.
如果你的整数很小,你最好用十六进制编码它们 - 见saua
否则(hacky递归版):
def convertIntToByteString(i): if i == 0: return "" else: return convertIntToByteString(i >> 8) + chr(i & 255)
您不需要base64编码,您希望在数字基数X中表示基数为10的数字.
如果您希望您的基数10数字以26个字母表示,您可以使用:http: //en.wikipedia.org/wiki/Hexavigesimal.(您可以通过使用所有合法的URL字符将该示例扩展为更大的基础)
你应该至少能得到38(26个字母,10个数字,+,_)