问题描述
我正在尝试估算可以用5个字符的base64、6个字符等等来编码多少个无符号整数。
通过编程方法,我发现自己可以编码
2^28 - 1 = 268,435,455
包含6个字符和
2^35 - 1 = 34,359,738,368
具有7个字符。
(-1因为我从uint 1开始)
尽管如此,我仍在努力进行概括,因为我假设它始于2^8 = 256
,但我不知道我如何最终到达28
和35
。
这是我在Go中的实现
func Shorten(num uint64) string {
buf := make([]byte,binary.MaxVarintLen64)
n := binary.PutUvarint(buf,num)
b := buf[:n]
encoded := base64.URLEncoding.EncodetoString(b)
return strings.Replace(encoded,"=","",-1)
}
也
0 -> AA
128 -> gAE
16384 -> gIAB
2097152 -> gICAAQ
268435456 -> gICAgAE
所以看起来它以7个增量上升:2 ^ 7、2 ^ 14、2 ^ 21等,但是为什么是7?
解决方法
一个字节是8位,因此是256个可能的值。 Base 64使用64个不同的字符进行编码,因此使用6位。那么6位中可以容纳多少8位对象?如果是四舍五入则为0,否则为3/4。当您开始谈论编码整数时,数字似乎没有意义。您是在谈论用ascii编写的整数吗?使用6个base64字符,您可以使用36位,因此,如果您谈论的是32位二进制无符号整数,则可以一次编码一个,但是您可以为2 ** 32种不同的可能性编码其中的任意一个,然后4个浪费的位。使用ascii,您将拥有4个字符,因此将有10000种不同的可能性(0到9999)。
由于使用不被编码为常规二进制整数的go varint,所以您得到了意外的结果。一些ipython输出给您:
In [22]: base64.b64encode((128).to_bytes(1,'little'))
Out[22]: b'gA=='
因为128可以被编码为单个8位字节,所以只有2个字符并带有一些填充。看看这个:
In [3]: base64.b64decode('gAE=')
Out[3]: b'\x80\x01'
In [4]: int.from_bytes(_,'little')
Out[4]: 384
因此,如您所见,PutUVarint不仅在编码可变长度的整数,还在编码一个可变整数,即,它已经以一种可以被解码的方式被编码,而无需事先知道它的大小。如果您查看source code for the varint go module,它将描述此过程。 Go使用每个字节的7位来保存实际的整数二进制数据,而最高有效位是关于是否还有更多数据要发送的标志。 128只是一个字节集的最高有效位。因此,基本上,您是根据完成此任务的方式进行两次编码的。如果您有一个给定的整数将其编码为var int,则需要该整数使用* 8/7来存储该值的字节数,然后您将base64对该结果进行编码,因此需要该值* 8/6来存储该值。根据您对base64的处理方式,您可能无需使用go varints就可以确定正在播放的字节数,然后计算结果将是8/6转换(我只是4/3保留它以更紧密地匹配varint过程。)