Base64编码原理与应用
本文內容轉自網絡,如需詳細內容,請參考相關網址。
http://my.oschina.net/goal/blog/201032
代碼參考:http://blog.csdn.net/prsniper/article/details/7097643
?
?
Base64,它用作把任意序列的8位字節描述為一種不易被人直接識別的形式,常用作開發中用于傳遞參數、瀏覽器的img標簽通過base64字符串來渲染圖片以及電子郵件的正文編碼等等。
在計算機中顯示的字符,比如英文字母、數字以及英文標點符號就是用一個字節來存儲,通常稱為ASCII碼。而簡體中文、繁體中文、日文以及韓文等都是用多字節來存儲的,通常稱之為多字節字符。因為Base編碼的輸入是字符串的編碼,不同編碼的字符串的Base64結果是不同的。所以,先來介紹基本的字符編碼知識。
字符編碼基本知識
最開始的計算機,只支持ASCII碼,不支持中文等其他字符,一個字符采用一個字節(8位)表示,只使用低7位,最高位固定為0,因此總共有128個ASCII碼(取值范圍:0~127)。
為了支持更多地區的語言,各大組織機構和IT廠商開始推廣自己的編碼方案,以彌補ASCII編碼的不足,如GB2312編碼、GBK編碼和Big5編碼,這些編碼只是針對局部地區的文字,往下兼容ASCII碼,沒辦法表達所有的語言,這些不同的編碼之間沒有任何聯系,他們之間的轉換需要通過查表來實現。
為了提高計算機的信息處理和交換能力,使得各國文字都能在計算機中處理。國際ISO組織制定了通用多字節編碼字符集(ISO 10646),簡稱UCS,這一標準為世界各種主要語言的字符以及附加符號,編制統一的內碼。Unicode是Unicode學術學會機構制定的編碼系統,從內容上來看,和UCS是同步一致的。
ANSI不代表具體的編碼,而是代指本地編碼,比如在簡體中文版的Windows上面,它代表GB2312編碼,在繁體中文版上面,它代表Big5編碼,在日文操作系統上面,代表JIS編碼。所以,當用戶新建并且保存文件類型為ANSI編碼,那么那會根據本地系統的編碼來確定具體的編碼。
Unicode編碼
Unicode編碼表和字符表是一一映射的,比如漢字”回“,其Unicode編碼為56DE,通過56DE就能在Unicode表中找到漢字”回“。Unicode本身定義了每個字符的數值,是字符和自然數的映射關系,而UTF-8、UTF-16則定義了如何在字節流中斷字。現在最為常用的Unicode編碼為UTF-8和UTF-16.下圖為常用的UTF-8的編碼形式。在線漢字編碼查詢點此進入。
從圖中可以看出,UTF-8為變長的編碼方式(1~6個字節),向下兼容ASCII編碼,通常將UTF-8看做單字節或三字節的實現,其他情況非常罕見。每個字節的開始很有規律,方便處理。
UTF-16
UTF-16編碼是最直接的Unicode的實現方式,它采用固定兩個字節來存儲,因為是多字節,所以有小端存儲和大端存儲兩種方式。UTF-16編碼是Windows上默認的Unocode編碼方式,兩個字節小端存儲。
在Windows的文本文檔中,當另存為時,在編碼類型中,有如下幾個選擇。
就同樣一個漢字”回“字,其不同的編碼內容如下:
unicode編碼結果上面,前面的兩個字節FF FE是文件頭,代表這是一個UTF16編碼的文件,DE 56是”回“字的UTF16編碼十六進制表示,低位字節為DE,高位字節為56,組合在一起,就是0x56DE。
有了上述的知識積累,就可以很方便的在UTF8和UTF16之間相互轉換了,還是以”回“字為例子,UTF16編碼值為? 0x56DE,它在0x0000_0800~0x0000_FFFF之間,對于的UTF8字節為三字節。所以,這個轉換就是將2字節的UTF16轉換為3字節的UTF8編碼。注意到UTF-8表中的轉換圖中的x部分,就是對應0x56DE的各個位的數值。轉化結果如下:
UTF8轉換為UTF16就是上面的逆過程,知道了轉換規則,很容易實現其代碼。
中國大陸使用的中文標準為GB2312,一共收錄了7445個常用簡體漢字和中文符號。
Big5是臺灣使用的編碼標準,大約編碼了8千多個繁體漢字。
HKSCS是香港地區使用的編碼標準,但和Big5有所不同。
上述這幾套中文編碼互不兼容,妨礙軟件開發,國際上針對此情況,制定了針對中文的統一字符集GBK和GB18030,其中,GBK已經在Windows、Linux等多種操作系統上實現。GBK1.0收錄了21886個符號,分為漢字區和圖形符號區,2000年的GB18030是取代GBK1.0,成為正式國家標準,該標準收錄了27484個漢字,還收錄了藏文、蒙文、維吾爾文等主要少數民族漢字。一般設備上,只需要支持GB2312就足夠了。
從ASCII、GB2312、GBK到GB18030,這些編碼是向下兼容的,其中,區分中文編碼的方法是高字節的最高位不為0.
Unicode只與ASCII兼容,與GB系列碼不兼容。例如,“漢”字的Unicode碼為6C49,GB碼為BABA。
?
上面講述了最基本的ASCII、UTF-8、UTF-16等編碼的基礎知識和對于的轉化規則,下面開始介紹本文重點內容Base64編碼。
?
Base64編碼
Base64用在必須用可打印字符表示二進制內容的場合,將任意字節轉為可讀字符的編碼,這種編碼,不是為安全,因為它是可逆的,而是為了顯示。比如需要在xml文檔中包含一段音頻或者數字簽名,URL傳遞參數,電子郵件的傳輸編碼,可打印字符包括大小寫字母(A-Z,a-z),數字(0-9),加號(“+”),正斜杠(“/”),外加補全符號(“=”)
Base64編碼要求把3個8位字節(3*8=24位)編碼成4個6位的字節(4*6=24位),之后在每個6位字節前面,補充兩個0,形成4個8位字節的形式(取值范圍在0~63),由于2^6次方等于64,所以每6個位組成一個單元,對于某個可打印的字符,當原始數據不是3的整數倍時,
當最后剩下一個輸入字節時,在編碼后面添加兩個”=”
當最后剩下兩個輸入字節時,在編碼后面添加一個“=”
當數據可以被3整除,就不需要添加數據。
下圖為Base64轉碼表:
上述為標準的Base64編碼,標準的Base64編碼不適合直接放在URL里面傳輸,因為URL編碼器會將”/“和”+”字符轉變為形如”%XX”的形式,而這些”%”號在存入數據庫時,還需要轉換,因為ANSI SQL中”%“是通配符。
人們為了解決此問題,提出了用于URL的改進Base64編碼,它不在末尾填充”=”,并且將標準Base64中的”+”和”/”分別改成了”-”和“_”,這樣就免去URL的編解碼和數據存儲時的格式轉換,長度保持不變,統一了數據庫、表單等處理對象的格式。
編碼過程,簡單下來可以總結為:
1. 先將輸入的字節數湊成3的整數倍N,然后申請N*4/3+1這么多的空間內存空間
2. 按照3個字節為一組,轉換為4個字節的輸出原則轉換,特別要注意申請空間的最后一位的處理
3. 解密時,有取值判斷和查表兩種方法,推薦使用查表法。
4. 在字符串輸出的最后,需要加上結束標志符’\0’
?
下面給出C語言的Base64代碼,在網友給出的代碼基礎上,經過自己認真測試,現在貢獻出來,希望能夠幫助到其他人。我在這里面,對結束符的處理是映射為0xFF,個人覺的,只要知道這個標志出現了,做適當的處理就可以了。
?
/**************************************************************************************************Filename: base64.cRevised: 2014-12-09 15.18Description: this file use to descript the Base64 encode and decodeAuthor: huhaoEmail: huhao0126@163.com ***************************************************************************************************/#include "stdlib.h" #include "base64.h"static const char BASE_CODE[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";static const unsigned char base64_dec_map[128] = {127, 127, 127, 127, 127, 127, 127, 127, 127, 127, //0 ~ 9127, 127, 127, 127, 127, 127, 127, 127, 127, 127, //10~19127, 127, 127, 127, 127, 127, 127, 127, 127, 127, //20~29127, 127, 127, 127, 127, 127, 127, 127, 127, 127, //30~39127, 127, 127, 62, 127, 127, 127, 63, 52, 53, //40~4954, 55, 56, 57, 58, 59, 60, 61, 127, 127, //50~59127, 0xFF, 0, 0, 0, 0, 1, 2, 3, 4, //60~695, 6, 7, 8, 9, 10, 11, 12, 13, 14, //70~7915, 16, 17, 18, 19, 20, 21, 22, 23, 24, //80~8925, 127, 127, 127, 127, 127, 127, 26, 27, 28, //90~9929, 30, 31, 32, 33, 34, 35, 36, 37, 38, //100~10939, 40, 41, 42, 43, 44, 45, 46, 47, 48, //110~11949, 50, 51, 127, 127, 127, 127, 127 //120~127 };/*************************************************************************************************** @fn base64_encode** @brief This function encode the bindata into base64 format.** input parameters** @param bindata - the input bindata* @param output - the output base64 data.* @param slen - the length of input data.** output parameters** None.** @return the length of encoder output***************************************************************************************************/ int Base64Encode(const unsigned char* bindata, unsigned char* output,int slen) {int vlen = 0;unsigned char* temp_data;temp_data = bindata;while(slen > 0 ){*output++ = BASE_CODE[ (temp_data[0]>>2) & 0x3F ];if(slen > 2 ) //長度大于三個字符 處理生成4個字符 {*output++ = BASE_CODE[ ( ( temp_data[0] & 0x03 )<<4) | ( temp_data[1] >>4) ];*output++ = BASE_CODE[ ( ( temp_data[1] & 0x0F )<<2) | ( temp_data[2] >>6) ];*output++ = BASE_CODE[ ( temp_data[2] & 0x3F )];}else if( slen == 2) //恰好為兩個字符 {*output++ = BASE_CODE[ ( ( temp_data[0] & 0x03 )<<4) | ( temp_data[1] >>4) ];*output++ = BASE_CODE[ ( ( temp_data[1] & 0x0F )<<2)];*output++ = '=';}else if( slen == 1) //恰好為一個字符 {*output++ = BASE_CODE[ (temp_data[0]&0x03) << 4];*output++ = '=';*output++ = '=';}temp_data += 3;slen -= 3;vlen += 4;}*output = '\0'; //this is very improtantreturn vlen; }/*************************************************************************************************** @fn GetCharIndex** @brief This function get the mapping value.** input parameters** @param c - Base64 code** output parameters* none* it has two ways to map from ciphertext to plaintext,one is lookup-table,and the other is value judgements.* @return original value ***************************************************************************************************/ unsigned char GetCharIndex(unsigned char c) {#if 1if( ( 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;}else if(c == '='){return 0xFF;}#elsereturn base64_dec_map[c];#endifreturn 0; }/*************************************************************************************************** @fn BaseDecode** @brief This function decode the bindata into base64 format.** input parameters** @param input - the encoded input data* @param output - the decode output data* @param sLen - the length of encoded input data.** output parameters* none** @return vlen - the length of decode data***************************************************************************************************/ int Base64Decode(const unsigned char* input,unsigned char *output,int sLen) {static unsigned char lpCode[4] = {0};unsigned char* data_temp = input;int vlen = 0;//Base64 length must be a multiple of 4 including '='if( sLen % 4 ){return -1;}while(sLen > 0 ){lpCode[0] = GetCharIndex(data_temp[0]);lpCode[1] = GetCharIndex(data_temp[1]);lpCode[2] = GetCharIndex(data_temp[2]);lpCode[3] = GetCharIndex(data_temp[3]);if( lpCode[3] == 0xFF ){if( lpCode[2] == 0xFF ) // if there has two '=' at the end {*output++ = (lpCode[0] << 2) | (lpCode[1] >>4);vlen +=1;break;}else // if there has one '=' at the end {*output++ = (lpCode[0] << 2) | (lpCode[1] >>4);*output++ = (lpCode[1] << 4) | (lpCode[2] >>2);vlen +=2;break;}}else{*output++ = (lpCode[0] << 2) | (lpCode[1] >>4);*output++ = (lpCode[1] << 4) | (lpCode[2] >>2);*output++ = (lpCode[2] << 6) | (lpCode[3]);data_temp+=4;sLen -=4;vlen +=3;}}*output = '\0'; //this is very improtantreturn vlen; }/* main test function */ int test_base64() {unsigned char* output_buffer = NULL;char input_str[100]={0};int allocate_len;int input_len;int output_len;printf("Please input string : \n ");scanf("%s",input_str);input_len = strlen(input_str);printf("the length of input is %d \n",strlen(input_str));//補齊字節數,使得輸出緩存為4的倍數,這樣來考慮,先把輸入長度補齊到3的倍數,然后將其乘以4再除以3allocate_len = ( input_len % 3 ) ? ( input_len + 3 - input_len%3 ) :( input_len);allocate_len = ( allocate_len * 4 )/3;output_buffer = (unsigned char*)malloc(allocate_len+1); //加1 很重要,用于結束編解碼的輸出字符串if(NULL == output_buffer){printf("allocate memory fail! \n");return -1;}else{printf("success allocate %d bytes. \n",allocate_len);memset(output_buffer,0,allocate_len);}output_len = Base64Encode(input_str,output_buffer,input_len);printf("Base64ENcode(\" %s \" ,encoding output length is %d \") \n encode content is %s \" \n\n",input_str,output_len,output_buffer);memset(input_str,0,sizeof(input_str));output_len = Base64Decode(output_buffer,input_str,output_len);printf("Base64Decode(\" %s \" ,decoding output length is %d \") \n decode content is %s \n",output_buffer,output_len,input_str);free(output_buffer); //release the allocated memoryoutput_buffer = NULL; }執行結果如下:
?
完成這篇博客,從基礎知識的準備到base64原理的了解,再到最終代碼的實現和調試,花費了一天的時間,具體原理不難,調試過程中,遇到了很多問題,深知,網上得來終覺淺,絕知此事要躬行。別人給出的代碼,自己如果不敲一片,字字斟酌,如果只是匆匆掃過,是怎么樣也不會體會別人的思路和方法。現在網絡資源異常豐富,我們在別人的基礎上,進行自己的改進和優化,博采眾長,提升自己。
對代碼來說,光說不練假把式,雖說是今天實現的只是一個小小的功能,把小功能步步都想清楚,各種情況都處理好,每天深入了解學習一個知識點,能夠解決一個問題,那這一天就很有價值,沒有白白度過。
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
轉載于:https://www.cnblogs.com/cherishui/p/4153396.html
總結
以上是生活随笔為你收集整理的Base64编码原理与应用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 查看Linux下网卡状态或 是否连接(转
- 下一篇: 数据结构之栈的应用:表达式求值