Base64长度计算?

Roy*_*mir 142 string base64 formula padding

看完base64 维基 ...

我想弄清楚公式是如何工作的:

给定长度为的字符串n,base64长度为在此输入图像描述

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

我已经知道base64长度必须是%4==0让解码器知道原始文本长度是多少.

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

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

题:

上述信息如何与输出长度相符 在此输入图像描述

Pau*_*l R 186

每个字符用于表示6位(log2(64) = 6).

因此,使用4个字符来表示4 * 6 = 24 bits = 3 bytes.

所以你需要4*(n/3)字符来表示n字节,这需要四舍五入到4的倍数.

由四舍五入到4的倍数产生的未使用的填充字符的数量显然将是0,1,2或3.

  • 我在上面的答案中解释了所有这些:(i)每个输出*char*代表输入的6*位*,(ii)4个输出*chars*因此代表4*6 = 24*位*,(iii)24*位*是3*字节*,(iv)输入的3*字节*因此导致输出的4*字符*,(v)输出*字符*与输入*字节*的比率因此是4/3. (5认同)
  • 考虑一下您是否有一个字节的输入。这将产生四个字符的输出。但只需要两个输出字符即可对输入进行编码。所以两个字符将被填充。 (3认同)
  • 填充物在哪里? (2认同)
  • 输出长度始终向上舍入为4的倍数,因此1,2或3个输入字节=> 4个字符; 4,5或6个输入字节=> 8个字符; 7,8或9个输入字节=> 12个字符. (2认同)
  • @ techie_28:我为20*1024字节制作了27308个字符,但今天早上我还没有喝咖啡. (2认同)

小智 51

4 * n / 3 给出了无衬垫的长度.

并且向上舍入到最接近的4的倍数用于填充,并且4是2的幂可以使用按位逻辑运算.

((4 * n / 3) + 3) & ~3
Run Code Online (Sandbox Code Playgroud)

  • `4 * n / 3` 在 `n = 1` 处已经失败,一个字节使用两个字符编码,结果显然是一个字符。 (3认同)
  • 为使用 shell 的人拼写出来:`$(( ((4 * n / 3) + 3) & ~3 ))` (2认同)

Dav*_*rtz 25

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

Base64编码器的长度公式

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

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

你在评论中询问编码的大小123456.请记住,该字符串的每个字符的大小都是1字节或8位(假设是ASCII/UTF8编码),我们编码6个字节或48位数据.根据等式,我们期望输出长度(6 bytes / 3 bytes) * 4 characters = 8 characters.

123456为Base64编码器创建MTIzNDU2,这是8个字符长,正如我们的预期.

  • 使用此公式,请注意它不会给出填充长度.所以你可以有更长的时间. (5认同)

Maa*_*wes 8

整型

通常我们不想使用双精度因为我们不想使用浮点运算,舍入误差等.它们不是必需的.

为此,最好记住如何执行上限划分: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));
    }
}
Run Code Online (Sandbox Code Playgroud)

内联

加厚

我们知道每3个字节(或更少)我们需要4个字符的块.那么公式变为(对于x = n和y = 3):

blocks = (bytes + 3 - 1) / 3
chars = blocks * 4
Run Code Online (Sandbox Code Playgroud)

或合并:

chars = ((bytes + 3 - 1) / 3) * 4
Run Code Online (Sandbox Code Playgroud)

你的编译器会优化掉3 - 1,所以只需保留它就可以保持可读性.

不用护垫

不常见的是unpadded变体,为此我们记得每个我们需要每个6位的字符,四舍五入:

bits = bytes * 8
chars = (bits + 6 - 1) / 6
Run Code Online (Sandbox Code Playgroud)

或合并:

chars = (bytes * 8 + 6 - 1) / 6
Run Code Online (Sandbox Code Playgroud)

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

chars = (bytes * 4 + 3 - 1) / 3
Run Code Online (Sandbox Code Playgroud)

不可读

如果您不相信您的编译器为您做最后的优化(或者如果您想混淆您的同事):

加厚

((n + 2) / 3) << 2
Run Code Online (Sandbox Code Playgroud)

不用护垫

((n << 2) | 2) / 3
Run Code Online (Sandbox Code Playgroud)

所以我们有两种逻辑计算方法,我们不需要任何分支,位操作或模运算 - 除非我们真的想要.

笔记:

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


nma*_*att 7

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

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

n × 8 输入位

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

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

这是没有填充。

通过填充,我们将其四舍五入为四的倍数输出字节:

ceil ( ceil ( n × 4 / 3) / 4) × 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
Run Code Online (Sandbox Code Playgroud)

最后,在 MIME Base64 编码的情况下,每 76 个输出字节需要两个额外的字节 (CR LF),根据是否需要终止换行符向上或向下舍入。


小智 5

我认为给定的答案忽略了原始问题的重点,即需要分配多少空间来适应给定长度为n字节的二进制字符串的base64编码.

答案是 (floor(n / 3) + 1) * 4 + 1

这包括填充和终止空字符.如果要进行整数运算,则可能不需要发言权.

包括填充,base64字符串需要四个字节用于原始字符串的每个三字节块,包括任何部分块.当添加填充时,字符串末尾的额外一个或两个字节仍将转换为base64字符串中的四个字节.除非您有非常具体的用途,否则最好添加填充,通常是等号.我在C中为空字符添加了一个额外的字节,因为没有它的ASCII字符串有点危险,你需要单独携带字符串长度.

  • 你的公式错了.考虑n = 3,预期结果(没有空填充)是4,但是你的公式返回8. (5认同)
  • 我也认为包括null终止符是愚蠢的,特别是因为我们在这里谈论.net. (5认同)

小智 5

这是一个函数,用于将编码的Base 64文件的原始大小计算为以KB为单位的字符串:

private Double calcBase64SizeInKBytes(String base64String) {
    Double result = -1.0;
    if(StringUtils.isNotEmpty(base64String)) {
        Integer padding = 0;
        if(base64String.endsWith("==")) {
            padding = 2;
        }
        else {
            if (base64String.endsWith("=")) padding = 1;
        }
        result = (Math.ceil(base64String.length() / 4) * 3 ) - padding;
    }
    return result / 1000;
}
Run Code Online (Sandbox Code Playgroud)


And*_*eas 5

对于所有讲 C 语言的人,请看一下这两个宏:

// calculate the size of 'output' buffer required for a 'input' buffer of length x during Base64 encoding operation
#define B64ENCODE_OUT_SAFESIZE(x) ((((x) + 3 - 1)/3) * 4 + 1) 

// calculate the size of 'output' buffer required for a 'input' buffer of length x during Base64 decoding operation
#define B64DECODE_OUT_SAFESIZE(x) (((x)*3)/4) 
Run Code Online (Sandbox Code Playgroud)

取自这里