Base64长度计算?

时间:2012-11-14 12:27:23

标签: string base64 padding formula

阅读base64 wiki ...

我正在试图找出如何使用该公式:

给定长度为n的字符串,base64长度为enter image description here

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

我已经知道base64长度必须是%4==0才能让解码器知道原始文本的长度。

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

  

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

问题:

以上信息如何以输出长度enter image description here结算?

15 个答案:

答案 0 :(得分:162)

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

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

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

由于四舍五入到4的倍数而产生的未使用填充字符数明显为0,1,2或3。

答案 1 :(得分:44)

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

向上舍入为最接近的4的倍数用于填充,而4为2的幂可以使用按位逻辑运算。

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

答案 2 :(得分:23)

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

Base64 encoder's length formula

正如您所说,给定n字节数据的Base64编码器将生成4n/3 Base64字符的字符串。换句话说,每3个字节的数据将产生4个Base64字符。 编辑评论正确地指出我之前的图片没有考虑填充;正确的公式是 Ceiling(4n/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 bytes / 3 bytes) * 4 characters = 8 characters

123456放入Base64编码器会创建MTIzNDU2,其长度为8个字符,正如我们预期的那样。

答案 3 :(得分:7)

整数

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

为此,最好记住如何执行天花板分割:双倍的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,所以请保持这样以保持可读性。

<强>未填充

不常见的是unpadded变体,为此我们记得每个我们需要每个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 :(得分:5)

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

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

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

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

答案 5 :(得分:4)

这是一个将编码的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;
}

答案 6 :(得分:4)

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

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

n ×8个输入位

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

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

这没有填充。

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

ceil ceil n ×4/3)/ 4)×4 = = ceil (< em> n ×4/3/4)×4 = ceil n / 3)×4输出字节

有关第一个等效信息,请参见Nested Divisions(维基百科)。

使用整数算术, ceil n / m 可以计算为(< em> 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),根据是否需要终止换行符将其四舍五入。

答案 7 :(得分:2)

在我看来,正确的公式应该是:

n64 = 4 * (n / 3) + (n % 3 != 0 ? 4 : 0)

答案 8 :(得分:1)

虽然其他人都在讨论代数公式,但我只是想用BASE64来告诉我:

$ echo "Including padding, a base64 string requires four bytes for every three-byte chunk of the original string, including any partial chunks. One or two bytes extra at the end of the string will still get converted to four bytes in the base64 string when padding is added. Unless you have a very specific use, it is best to add the padding, usually an equals character. I added an extra byte for a null character in C, because ASCII strings without this are a little dangerous and you'd need to carry the string length separately."| wc -c

525

$ echo "Including padding, a base64 string requires four bytes for every three-byte chunk of the original string, including any partial chunks. One or two bytes extra at the end of the string will still get converted to four bytes in the base64 string when padding is added. Unless you have a very specific use, it is best to add the padding, usually an equals character. I added an extra byte for a null character in C, because ASCII strings without this are a little dangerous and you'd need to carry the string length separately." | base64 | wc -c

710

所以看起来3个字节的公式由4个base64字符表示似乎是正确的。

答案 9 :(得分:1)

我认为如果n%3不为零,那么这个是一个确切的答案,不是吗?

    (n + 3-n%3)
4 * ---------
       3

Mathematica版本:

SizeB64[n_] := If[Mod[n, 3] == 0, 4 n/3, 4 (n + 3 - Mod[n, 3])/3]

玩得开心

GI

答案 10 :(得分:1)

我没有在其他回复中看到简化的公式。涵盖了逻辑,但是我想要一种最基本的形式供嵌入式使用:

  Unpadded = ((4 * n) + 2) / 3

  Padded = 4 * ((n + 2) / 3)

注意:在计算未填充的计数时,我们会四舍五入整数除法,即在这种情况下,将Divisor-1添加为+2

答案 11 :(得分:0)

在Windows中 - 我想估计mime64大小的缓冲区的大小,但是所有精确的计算公式对我来说都不起作用 - 最后我最终得到了这样的近似公式:

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

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

答案 12 :(得分:0)

使用javascript实现简单实现

function sizeOfBase64String(base64String) {
    if (!base64String) return 0;
    const padding = (base64String.match(/(=*)$/) || [])[1].length;
    return 4 * Math.ceil((base64String.length / 3)) - padding;
}

答案 13 :(得分:0)

如果有人有兴趣在JS中实现@Pedro Silva解决方案,我只是为此移植了相同的解决方案:

const getBase64Size = (base64) => {
  let padding = base64.length
    ? getBase64Padding(base64)
    : 0
  return ((Math.ceil(base64.length / 4) * 3 ) - padding) / 1000
}

const getBase64Padding = (base64) => {
  return endsWith(base64, '==')
    ? 2
    : 1
}

const endsWith = (str, end) => {
  let charsFromEnd = end.length
  let extractedEnd = str.slice(-charsFromEnd)
  return extractedEnd === end
}

答案 14 :(得分:0)

对于所有讲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) 

来自here