原創文章,歡迎轉載。轉載請註明出處:http://blog.csdn.net/jmppok/article/details/17096685c++
Base64是一種基於64個可打印字符來表示二進制數據的表示方法。因爲2的6次方等於64,因此每6位爲一個單元,對應某個可打印字符。在Base64中的可打印字符包括字母A-Z、a-z、數字0-9,這樣共有62個字符,此外兩個可打印符號在不一樣的系統中而不一樣。oop
具體可參照維基百科:維基百科 Base64性能
在計算機使用過程當中,常常會遇到Binary數據,即一個數據塊,咱們知道其首地址和長度,經過指針便可對其進行快速訪問。測試
但實際環境中,咱們又常常須要將這個數據轉換爲String字符串,而在String字符串中有一個規則,遇到'\0'字符便認爲字符串結束。咱們不能保證Binary數據中有沒有'\0'字符,因此直接將Binary數據賦值給String即可能出現問題。編碼
而解決這一問題的辦法就是使用Base64編碼,上面說到Base64編碼只有64個字符,'A-Z' 'a-z' '0-9' + /。沒有'\0'字符,咱們只要先辦Binary轉爲Base64編碼格式,便可避免出現'\0'字符,從而在賦值給String時變得可靠。
.net
二機制數據經過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 |
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,所以須要加上'==')
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; }
測試環境:普通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編碼耗時與數據大小成線形比例,在數據量不大時耗時很小。
1)該測試程序爲本人隨手而寫,還有許多改進空間;以此程序進行測試的結果,僅做參考。
2)因爲時間有限,未進行Base64解碼的測試。
在這篇文章中進行了改進,encode效率提升8倍左右。