仅编码无符号整数时的base64字符串长度计算

问题描述

我正在尝试估算可以用5个字符的base64、6个字符等等来编码多少个无符号整数。

通过编程方法,我发现自己可以编码

2^28 - 1 = 268,435,455

包含6个字符和

2^35 - 1 = 34,359,738,368

具有7个字符。

(-1因为我从uint 1开始)

尽管如此,我仍在努力进行概括,因为我假设它始于2^8 = 256,但我不知道我如何最终到达2835

这是我在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过程。)