在阅读base64维基之后…
我想知道这个公式是怎么运作的
给定一个长度为n的字符串,base64的长度为
即:4*Math.Ceiling(((double)s.Length/3)))
我已经知道base64的长度必须是%4==0,以允许解码器知道原始文本的长度。
序列的最大填充数可以是=或==。
wiki:每个输入字节的输出字节数大约是4 / 3 (33%) 开销)
问题:
以上信息是如何与输出长度相匹配的?
在阅读base64维基之后…
我想知道这个公式是怎么运作的
给定一个长度为n的字符串,base64的长度为
即:4*Math.Ceiling(((double)s.Length/3)))
我已经知道base64的长度必须是%4==0,以允许解码器知道原始文本的长度。
序列的最大填充数可以是=或==。
wiki:每个输入字节的输出字节数大约是4 / 3 (33%) 开销)
问题:
以上信息是如何与输出长度相匹配的?
当前回答
对于所有会说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)
从这里拍的。
其他回答
整数
通常我们不想使用双精度数,因为我们不想使用浮点运算,舍入错误等。他们只是没有必要。
为此,最好记住如何执行上限除法:双数的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,您可能需要注意可能的行终止字符等(寻找其他答案)。
如果有人有兴趣在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
}
在我看来,正确的公式应该是:
n64 = 4 * (n / 3) + (n % 3 != 0 ? 4 : 0)
下面是一个函数来计算一个base64编码文件的原始大小为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;
}
在windows中-我想估计mime64大小的缓冲区的大小,但所有精确的计算公式都不适合我-最后我得到了这样的近似公式:
Mine64字符串分配大小(近似) =((4 *((二进制缓冲区大小)+ 1))/ 3)+ 1)
所以最后+1 -它用于ascii- 0 -最后一个字符需要分配来存储零结束-但为什么“二进制缓冲区大小”是+1 -我怀疑有一些mime64终止字符?或者这可能是一些对齐问题。