Base64编码及编码性能测试

原创文章,欢迎转载。转载请注明出处:http://blog.csdn.net/jmppok/article/details/17096685


1.什么是Base64编码


Base64是一种基于64个可打印字符来表示二进制数据的表示方法。由于2的6次方等于64,所以每6位为一个单元,对应某个可打印字符。在Base64中的可打印字符包括字母A-Z、a-z、数字0-9,这样共有62个字符,此外两个可打印符号在不同的系统中而不同。


具体可参照维基百科:维基百科 Base64


2.为什么要使用Base64编码

在计算机使用过程中,经常会遇到Binary数据,即一个数据块,我们知道其首地址和长度,通过指针即可对其进行快速访问。

但实际环境中,我们又经常需要将这个数据转换为String字符串,而在String字符串中有一个规则,遇到'\0'字符便认为字符串结束。我们不能保证Binary数据中有没有'\0'字符,所以直接将Binary数据赋值给String便可能出现问题。

而解决这一问题的办法就是使用Base64编码,上面说到Base64编码只有64个字符,'A-Z' 'a-z' '0-9' + /。没有'\0'字符,我们只要先办Binary转为Base64编码格式,即可避免出现'\0'字符,从而在赋值给String时变得可靠。

3.编码规则

二机制数据通过8位表示一个字符,Base64通过6位表示一个字符。所以将一个二进制数据转换为Base64编码,其长度要增加 (lengh*8/6)倍。

其规则如下:

1)依次从Binary数据中取出6位,转换为一个Base64字符;

2)如果最后有剩余则以0补够6位。在补位的同时,需要在Base64结尾以‘=’进行标识,一个'='表示补2个0,两个'==',表示补4个0。

需要注意的是第二条规则,由于数据本身是8的倍数,所以每次取6位,最后的余数只能是2或4。这两个则通过在Base64结尾加‘=’和'=='标识。


下面是一个Base64编码索引表和补位的说明:

Base64索引表:

ValueChar ValueChar ValueChar ValueChar
0A16Q32g48w
1B17R33h49x
2C18S34i50y
3D19T35j51z
4E20U36k520
5F21V37l531
6G22W38m542
7H23X39n553
8I24Y40o564
9J25Z41p575
10K26a42q586
11L27b43r597
12M28c44s608
13N29d45t619
14O30e46u62+
15P31f47v63/

如果要编码的字节数不能被3整除,最后会多出1个或2个字节,那么可以使用下面的方法进行处理:先使用0字节值在末尾补足,使其能够被3整除,然后再进行base64的编码。在编码后的base64文本后加上一个或两个'='号,代表补足的字节数。也就是说,当最后剩余一个八位字节(一个byte)时,最后一个6位的base64字节块有四位是0值,最后附加上两个等号;如果最后剩余两个八位字节(2个byte)时,最后一个6位的base字节块有两位是0值,最后附加一个等号。 参考下表:

文本(1 Byte)A  
二进制位01000001                
二进制位(补0)010000010000            
Base64编码QQ  
文本(2 Byte)BC 
二进制位0100001001000011  xxxxxx
二进制位(补0)010000100100001100xxxxxx
Base64编码QkM 



4.举例说明

1) 0 1 2编码为Base64

0 1 2 二进制表示为 0011 0000        0011  0001     0011  0010

转换为Base64为    001100          000011          000100              110010 

                           即    12                        3                   4                         50

从编码表中查找       M                          D                  E                          y

所以对应的Base64编码为 "MDEy"


2) 0123编码为Base64(需要补位)

0 1  2  3 二进制表示为 0011 0000        0011  0001     0011  0010        0011  0011

转换为Base64为    001100          000011          000100              110010   001100     110000(后面补4个0)

                           即    12                        3                   4                         50              12              48

从编码表中查找       M                          D                  E                          y                 M                w

所以对应的Base64编码为 "MDEyMw==" (由于补了4个0,因此需要加上'==')


5.C++代码


包括Base64.h 和Base64.cpp 以及一个测试程序 main.cpp

Base64.h

#ifndef BASE64_H_
#define BASE64_H_

void base64_encode(const char * pData, int len, char * pOut);

void base64_decode(const char * pData, int len, char * pOut);

int calc_base64_len(int data_len);
int calc_data_len(const char * base64, int base64_len);

#endif


Base64.cpp

#include "Base64.h"


int calc_base64_len(int data_len)
{
	int last = data_len*8%6;
	if(last == 0)
	{
		return data_len*8/6;
	}
	else
	{
		int base64_len = data_len*8/6+1;
		base64_len += (last==2?2:1);
		return base64_len;
	}
}
int calc_data_len(const char * base64, int base64_len)
{
	int dec = 0;
	while(base64[base64_len-1-dec]== '=')
	{
		dec++;
	}
	
	return ((base64_len-dec)*6)/8;
}

char _getBase64Char(char c)
{
	if(c>=0 && c<26)
	{
		return 'A'+c;
	}
	else if(c>=26 && c<52)
	{
		return 'a'+c-26;
	}
	else if(c>=52 && c<62)
	{
		return '0'+c-52;
	}
	else if(c==62)
	{
		return '+';
	}
	else if(c==63)
	{
		return '/';
	}
		
}

char _getCharOfBase64(char c)
{
	if(c>='A' && c<='Z')
	{
		return c-'A';
	}
	else if(c>='a' && c<='z')
	{
		return c-'a'+26;
	}
	else if(c>='0' && c<='9')
	{
		return c-'0'+52;
	}
	else if(c=='+')
	{
		return 62;
	}
	else if(c=='/')
	{
		return 63;
	}
}

void base64_encode(const char * pData, int len, char * pOut)
{
	int index = 0;
	while(len-index>=3)
	{
		const char * p1 = pData+index;
		const char * p2 = p1+1;
		const char * p3 = p2+1;
		
		char c1 = ((*p1) & (0b11111100)) >>2;
		char c2 = ((*p1) & (0b00000011))<<4 | ((*p2) & 0b11110000)>>4;
		char c3 = ((*p2) & (0b00001111))<<2 | ((*p3) & 0b11000000)>>6;
		char c4 = (*p3) & (0b00111111);
		
		*(pOut++)=_getBase64Char(c1);
		*(pOut++)=_getBase64Char(c2);
		*(pOut++)=_getBase64Char(c3);
		*(pOut++)=_getBase64Char(c4);

		index+=3;
	}
	
	int last = len-index;
	if(last==1)
	{
		const char * p1 = pData + index;
		char c1 = ((*p1) & (0b11111100)) >>2;
		char c2 = ((*p1) & (0b00000011))<<4;
		*(pOut++)=_getBase64Char(c1);
		*(pOut++)=_getBase64Char(c2);
		*(pOut++)='=';
		*(pOut++)='=';
	
	}
	else if(last == 2)
	{
		const char * p1 = pData+index;
		const char * p2 = p1+1;
		char c1 = ((*p1) & (0b11111100)) >>2;
		char c2 = ((*p1) & (0b00000011))<<4 | ((*p2) & 0b11110000)>>4;
		char c3 = ((*p2) & (0b00001111))<<2 ;
		
		*(pOut++)=_getBase64Char(c1);
		*(pOut++)=_getBase64Char(c2);
		*(pOut++)=_getBase64Char(c3);
		*(pOut++)='=';
		//*(pOut++)='=';
	}
		
}

void base64_decode(const char * pData, int len, char * pOut)
{
	int dec = 0;
	while(pData[len-1-dec]== '=')
	{
		dec++;
	}
	
	
	int real_len = len-dec;
	int index=0;
	while(real_len-index>=4)
	{
		const char * p1 = pData+index;
		const char * p2 = p1+1;
		const char * p3 = p2+1;
		const char * p4 = p3+1;
		
		char c1 = _getCharOfBase64(*p1);
		char c2 = _getCharOfBase64(*p2);
		char c3 = _getCharOfBase64(*p3);
		char c4 = _getCharOfBase64(*p4);
		
		*(pOut++)=(c1)<<2 | ((c2)&(0b00110000))>>4;
		*(pOut++)=((c2)&(0b00001111))<<4 | ((c3)&(0b00111100))>>2;
		*(pOut++)=((c3)&(0b00000011))<<6 | (c4)&(0b00111111);
		
		
		index+=4;
	}
	
	int last = real_len-index;
	if(last == 2)
	{
		const char * p1 = pData+index;
		const char * p2 = p1+1;
		
		char c1 = _getCharOfBase64(*p1);
		char c2 = _getCharOfBase64(*p2);
		
		*(pOut++)=(c1)<<2 | ((c2)&(0b00110000))>>4;
		
	}
	else if(last ==3)
	{
		const char * p1 = pData+index;
		const char * p2 = p1+1;
		const char * p3 = p2+1;
		
		char c1 = _getCharOfBase64(*p1);
		char c2 = _getCharOfBase64(*p2);
		char c3 = _getCharOfBase64(*p3);
		
		*(pOut++)=(c1)<<2 | ((c2)&(0b00110000))>>4;
		*(pOut++)=((c2)&(0b00001111))<<4 | ((c3)&(0b00110000))>>2;

	}
	
}


main.cpp

#include <stdio.h>
#include "time.h"
#include "Base64.h"

const char * file_in = "c:\\temp\\1.jpg";
const char * file_mid = "c:\\temp\\1.base64";
const char * file_out="c:\\temp\\11.jpg";

int main(int argc, char **argv)
{

	char * pData;
	int len;
	FILE * fp = fopen(file_in,"rb");
	if(fp)
	{
		fseek(fp,0,SEEK_END);
		len = ftell(fp);
		fseek(fp,0,SEEK_SET);
		
		pData = new char[len];
		fread(pData,1,len,fp);
		fclose(fp);
	}
	
	int base64_len = calc_base64_len(len);
	//int base64_len = len*8/6+1;
	//base64_len += (len*8%6)/2; //'='

	
	char * pOut = new char[base64_len];
	//encodeBase64(pData,len,pOut);
	base64_encode(pData,len,pOut);
	/*
	clock_t begin,end;
	begin = clock();
	int loop=0;
	while(loop<1000)
	{
	base64_encode(pData,len,pOut);
	loop++;
	}
	end = clock();
	printf("time used %d ms.\n",(end-begin));
	return 0;
	*/
	 
	//pOut[base64_len-1]='\0';
	fp = fopen(file_mid,"wb");
	if(fp)
	{
		fwrite(pOut,1,base64_len, fp);
		fclose(fp);
	}
	
	if(pData)
	{
		delete []pData;
	}
	
	if(pOut)
	{
		delete []pOut;
	}
	
	// read
	fp = fopen(file_mid,"rb");
	if(fp)
	{
		fseek(fp,0,SEEK_END);
		base64_len = ftell(fp);
		fseek(fp,0,SEEK_SET);
		
		pData = new char[base64_len];
		fread(pData,1,base64_len,fp);
		fclose(fp);
	}
	
	//int dec = 0;
	//while(pData[base64_len-1-dec]== '=')
	//{
	//	dec++;
	//}
	
	//len = ((base64_len-dec)*6)/8;
	len = calc_data_len(pData,base64_len);
	pOut = new char[len];
	//decodeBase64(pData,base64_len,pOut);
	base64_decode(pData,base64_len,pOut);
	fp = fopen(file_out,"wb");
	if(fp)
	{
		fwrite(pOut,1,len,fp);
		fclose(fp);
	}
	
	if(pData)
	{
		delete []pData;
	}
	
	if(pOut)
	{
		delete []pOut;
	}
	
	return 0;
}

main.cpp读取c:\temp\1.jpg,进行Base64编码,将编码结果写入c:\temp\1.base64, 然后再从c:\temp\1.base64中读取,解码还原,写入 c:\temp\11.jpg.


6.Base64编码性能测试

测试环境:普通PC机   i5 3470 3.2GGhz,内存4G,可用3.76  ,win7  32位

使用上述代码,编译环境为mingw。

只测试了Base64 encode的性能,encode1000次,取平均时间:

100K大小数据                               1.014ms

200K大小数据                               1.981ms

300K                                                2.917ms              

500K                                                4.961ms

1M                                                     10.624ms


总结:Base64编码耗时与数据大小成线形比例,在数据量不大时耗时很小。




7.总结与改进 

1)该测试程序为本人随手而写,还有许多改进空间;以此程序进行测试的结果,仅作参考。

2)由于时间有限,未进行Base64解码的测试。


在这篇文章中进行了改进,encode效率提高8倍左右。

http://blog.csdn.net/jmppok/article/details/17137839

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 数据报编码和解码需求分析需要考虑以下几个方面: 1. 数据格式:需要确定数据报的格式,例如数据报头的长度、数据报体的长度、数据报头和数据报体的字段、字段的数据类型、长度等。 2. 编码和解码算法:需要选择合适的编码和解码算法,例如Base64、JSON、XML、Protobuf等,确保编码和解码的准确性和效率。 3. 数据报的传输方式:需要确定数据报的传输方式,例如TCP或UDP协议。 4. 数据报的完整性和安全性:需要确保数据报的完整性和安全性,例如对数据报进行加密、校验和等操作。 5. 系统性能:需要考虑数据报的大小、传输的频率和并发数等因素,确保系统的性能和稳定性。 在需求分析的过程中,需要与相关的利益相关者进行沟通和协商,确保数据报的设计满足各方需求和期望。同时,需要进行充分的测试和验证,确保编码和解码的准确性和稳定性。 ### 回答2: 数据报编码和解码需求分析是指对数据报进行编码和解码的功能需求分析过程。 数据报编码需求分析主要包括以下几个方面: 首先是数据报的格式要求,即确定数据报的格式类型和结构,例如是纯文本还是二进制格式,以及数据报中需要包含哪些字段和信息; 其次是数据报的压缩需求,即对数据进行压缩处理,以减少数据报的大小,提高传输效率; 还有数据报的安全需求,包括对数据进行加密和解密,确保数据的机密性和完整性; 此外,还需要考虑数据报的兼容性需求,即能够处理不同格式和版本的数据报。 数据报解码需求分析主要包括以下几个方面: 首先是解析数据报的格式,根据数据报的结构和字段信息,对数据进行解析和拆解,以获取其中的有效数据; 其次是解压缩数据报,对经过压缩处理的数据报进行解压缩,还原为原始的数据; 还有数据报的安全性检验,包括对数据报进行解密和校验,确保数据的安全性和完整性; 此外,还需要考虑数据报的合法性检查,即判断数据报是否符合规定的格式和要求。 总之,数据报编码和解码需求分析是对数据报进行编码和解码过程中的功能需求进行详细分析,以确保数据的格式、安全和有效性,并提供相应的处理和解析功能。 ### 回答3: 数据报编码是将数据转换为二进制格式的过程,而解码则是将二进制数据转换回原始数据的过程。在进行数据报编码和解码的需求分析中,以下几个方面需要考虑: 首先,需要明确数据报的格式和结构,包括数据的类型、长度、顺序等。这可以通过对数据报的具体用途和要求进行分析来确定。比如,如果数据报用于网络传输,需要考虑网络协议和数据传输的需求,确定数据报的格式和结构。 其次,需考虑不同编码和解码算法的选择。不同的数据报可能需要采用不同的编码方式,如ASCII编码、UTF-8编码等,也需要相应的解码算法来还原数据。选择编码和解码算法时,应考虑数据的性质、大小、传输效率等因素。 另外,需分析数据报的安全性需求。数据报可能包含敏感信息,如密码、身份证号等,需要进行加密处理,确保数据的安全性。此时,需考虑选择适合的加密算法,并将其集成到编码和解码过程中。 最后,对于编码和解码的性能需求,需要分析数据报的使用场景和频率,确保编码和解码过程的效率和速度。这可以通过对数据处理的吞吐量、延迟等指标进行评估,并对编码和解码算法进行优化来实现。 通过对数据报编码和解码需求的分析,可以确保数据报在传输和处理中的准确性、安全性和效率性。同时,还可以根据具体需求选择适当的编码和解码算法,提供更好的用户体验和数据保护。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值