在阅读base64维基之后…

我想知道这个公式是怎么运作的

给定一个长度为n的字符串,base64的长度为

即:4*Math.Ceiling(((double)s.Length/3)))

我已经知道base64的长度必须是%4==0,以允许解码器知道原始文本的长度。

序列的最大填充数可以是=或==。

wiki:每个输入字节的输出字节数大约是4 / 3 (33%) 开销)

问题:

以上信息是如何与输出长度相匹配的?


当前回答

4 * n / 3为无填充长度。

并四舍五入到最接近4的倍数进行填充,因为4是2的幂,可以使用逐位逻辑运算。

((4 * n / 3) + 3) & ~3

其他回答

作为参考,Base64编码器的长度公式如下:

正如你所说的,给定n个字节的数据,一个Base64编码器将产生一个4n/3个Base64字符的字符串。换句话说,每3个字节的数据将导致4个Base64字符。编辑:一个评论正确地指出,我之前的图形没有说明填充;正确的填充公式是4(Ceiling(n/3))。

维基百科的文章在示例中准确地展示了ASCII字符串Man如何编码为Base64字符串TWFu。输入字符串的大小是3字节,或24位,因此公式正确地预测输出将是4字节(或32位)长:TWFu。该过程将每6位数据编码为64个Base64字符中的一个,因此24位输入除以6得到4个Base64字符。

您在注释中询问编码123456的大小。请记住,该字符串的每个字符的大小都是1字节或8位(假设ASCII/UTF8编码),我们正在编码6字节或48位的数据。根据公式,我们期望输出长度为(6字节/ 3字节)* 4个字符= 8个字符。

将123456放入Base64编码器中创建MTIzNDU2,正如我们预期的那样,它有8个字符长。

(试图给出一个简洁而完整的推导。)

每个输入字节有8位,所以对于n个输入字节,我们得到:

N × 8输入位

每6位是一个输出字节,因此:

ceil (n - 6) = ceil (n×8×4 - 3 )      输出字节

这是没有填充的。

对于填充,我们四舍五入为四个输出字节中的多个:

ceil (ceil (n×4 / 3))×4 = ceil (n×4 / 3 - 4)×4 = ceil (n - 3)×4字节输出

见嵌套划分(维基百科)的第一个等价。

使用整数算术,ceil(n / m)可以计算为(n + m - 1) div m, 因此我们得到:

(n * 4 + 2) div 3没有填充 (n + 2) div 3 * 4与填充

说明:

 n   with padding    (n + 2) div 3 * 4    without padding   (n * 4 + 2) div 3 
------------------------------------------------------------------------------
 0                           0                                      0
 1   AA==                    4            AA                        2
 2   AAA=                    4            AAA                       3
 3   AAAA                    4            AAAA                      4
 4   AAAAAA==                8            AAAAAA                    6
 5   AAAAAAA=                8            AAAAAAA                   7
 6   AAAAAAAA                8            AAAAAAAA                  8
 7   AAAAAAAAAA==           12            AAAAAAAAAA               10
 8   AAAAAAAAAAA=           12            AAAAAAAAAAA              11
 9   AAAAAAAAAAAA           12            AAAAAAAAAAAA             12
10   AAAAAAAAAAAAAA==       16            AAAAAAAAAAAAAA           14
11   AAAAAAAAAAAAAAA=       16            AAAAAAAAAAAAAAA          15
12   AAAAAAAAAAAAAAAA       16            AAAAAAAAAAAAAAAA         16

最后,在MIME Base64编码的情况下,每76个输出字节需要两个额外的字节(CR LF),这取决于是否需要一个结束换行符。

在windows中-我想估计mime64大小的缓冲区的大小,但所有精确的计算公式都不适合我-最后我得到了这样的近似公式:

Mine64字符串分配大小(近似) =((4 *((二进制缓冲区大小)+ 1))/ 3)+ 1)

所以最后+1 -它用于ascii- 0 -最后一个字符需要分配来存储零结束-但为什么“二进制缓冲区大小”是+1 -我怀疑有一些mime64终止字符?或者这可能是一些对齐问题。

整数

通常我们不想使用双精度数,因为我们不想使用浮点运算,舍入错误等。他们只是没有必要。

为此,最好记住如何执行上限除法:双数的ceil(x / y)可以写成(x + y - 1) / y(同时避免负数,但要注意溢出)。

可读的

如果你追求可读性,你当然也可以像这样编程(例如在Java中,对于C你当然可以使用宏):

public static int ceilDiv(int x, int y) {
    return (x + y - 1) / y;
}

public static int paddedBase64(int n) {
    int blocks = ceilDiv(n, 3);
    return blocks * 4;
}

public static int unpaddedBase64(int n) {
    int bits = 8 * n;
    return ceilDiv(bits, 6);
}

// test only
public static void main(String[] args) {
    for (int n = 0; n < 21; n++) {
        System.out.println("Base 64 padded: " + paddedBase64(n));
        System.out.println("Base 64 unpadded: " + unpaddedBase64(n));
    }
}

内联

我们知道每3个字节(或更少)需要4个字符块。那么公式就变成了(对于x = n, y = 3)

blocks = (bytes + 3 - 1) / 3
chars = blocks * 4

或结合:

chars = ((bytes + 3 - 1) / 3) * 4

你的编译器会优化出3 - 1,所以就这样保持可读性。

填充物的

不太常见的是无填充的变体,为此我们记得每6位都需要一个字符,四舍五入:

bits = bytes * 8
chars = (bits + 6 - 1) / 6

或结合:

chars = (bytes * 8 + 6 - 1) / 6

然而,我们仍然可以除以2(如果我们愿意的话):

chars = (bytes * 4 + 3 - 1) / 3

不可读的

如果你不相信你的编译器会为你做最终的优化(或者如果你想让你的同事困惑):

((n + 2) / 3) << 2

填充物的

((n << 2) | 2) / 3

所以我们有两种逻辑计算方式,我们不需要任何分支,位运算或模运算,除非我们真的想这样做。

注:

显然,您可能需要在计算中添加1以包含空终止字节。 对于Mime,您可能需要注意可能的行终止字符等(寻找其他答案)。

4 * n / 3为无填充长度。

并四舍五入到最接近4的倍数进行填充,因为4是2的幂,可以使用逐位逻辑运算。

((4 * n / 3) + 3) & ~3