Base64編碼及編碼性能測試

原創文章,歡迎轉載。轉載請註明出處:http://blog.csdn.net/jmppok/article/details/17096685c++


1.什麼是Base64編碼


Base64是一種基於64個可打印字符來表示二進制數據的表示方法。因爲2的6次方等於64,因此每6位爲一個單元,對應某個可打印字符。在Base64中的可打印字符包括字母A-Z、a-z、數字0-9,這樣共有62個字符,此外兩個可打印符號在不一樣的系統中而不一樣。oop


具體可參照維基百科:維基百科 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時變得可靠。
.net

3.編碼規則

二機制數據經過8位表示一個字符,Base64經過6位表示一個字符。因此將一個二進制數據轉換爲Base64編碼,其長度要增長 (lengh*8/6)倍。指針

其規則以下:
code

1)依次從Binary數據中取出6位,轉換爲一個Base64字符;blog

2)若是最後有剩餘則以0補夠6位。在補位的同時,須要在Base64結尾以‘=’進行標識,一個'='表示補2個0,兩個'==',表示補4個0。索引

須要注意的是第二條規則,因爲數據自己是8的倍數,因此每次取6位,最後的餘數只能是2或4。這兩個則經過在Base64結尾加‘=’和'=='標識。


下面是一個Base64編碼索引表和補位的說明:

Base64索引表:

Value Char   Value Char   Value Char   Value Char
0 A 16 Q 32 g 48 w
1 B 17 R 33 h 49 x
2 C 18 S 34 i 50 y
3 D 19 T 35 j 51 z
4 E 20 U 36 k 52 0
5 F 21 V 37 l 53 1
6 G 22 W 38 m 54 2
7 H 23 X 39 n 55 3
8 I 24 Y 40 o 56 4
9 J 25 Z 41 p 57 5
10 K 26 a 42 q 58 6
11 L 27 b 43 r 59 7
12 M 28 c 44 s 60 8
13 N 29 d 45 t 61 9
14 O 30 e 46 u 62 +
15 P 31 f 47 v 63 /

若是要編碼的字節數不能被3整除,最後會多出1個或2個字節,那麼可使用下面的方法進行處理:先使用0字節值在末尾補足,使其可以被3整除,而後再進行base64的編碼。在編碼後的base64文本後加上一個或兩個'='號,表明補足的字節數。也就是說,當最後剩餘一個八位字節(一個byte)時,最後一個6位的base64字節塊有四位是0值,最後附加上兩個等號;若是最後剩餘兩個八位字節(2個byte)時,最後一個6位的base字節塊有兩位是0值,最後附加一個等號。 參考下表:

文本(1 Byte) A    
二進制位 0 1 0 0 0 0 0 1                                
二進制位(補0) 0 1 0 0 0 0 0 1 0 0 0 0                        
Base64編碼 Q Q    
文本(2 Byte) B C  
二進制位 0 1 0 0 0 0 1 0 0 1 0 0 0 0 1 1     x x x x x x
二進制位(補0) 0 1 0 0 0 0 1 0 0 1 0 0 0 0 1 1 0 0 x x x x x x
Base64編碼 Q k M  



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

相關文章
相關標籤/搜索