Base64編碼是一種很經常使用的編碼,在RSA、AES等加密算法中,密鑰對的表示一般使用Base64,UTF-7也是在Base64的基礎上變化而來,固然全部僅支持ASCII碼傳輸的網關在傳輸非ASCII碼時,均可以使用Base64編碼。javascript
Base64編碼的方法很是簡單,將3個Byte共24Bit從高到低從新拆分紅4部分每部分6Bit,分別爲0x0~0x3f,對應字符爲A~Z和a~z和0-9和+/,共64個。若是最後剩餘1個Byte,則將其編碼爲2個6Bit的Base64編碼(第二個Base64編碼僅2Bit,需在其後面添加4Bit的0),再在末尾添加2個=字符;若是最後剩餘2個Byte,則將其編碼爲3個6Bit的Base64編碼(第三個Base64編碼僅4Bit,需在其後面添加2Bit的0),再在末尾添加1個=字符。css
Base64解碼的方法與編碼相反,將4個Base64編碼字符轉換爲對應的0x0~0x3f,共24Bit,而後從新拆分紅3部分,每部分8Bit,即1Byte,若末尾有=字符,則按編碼方法中描述的規則反向處理。html
網上有不少現成的算法,但彷佛沒有使用JavaScript實現的,實際上使用JavaScript實現也不是太複雜。去年我就寫了這個程序,只是忘記放在這裏了,前天給學生講課(放假前的最後一課,開學就大四了,可編程的能力尚需錘鍊),講到了這個,特地加了不少註釋,放在這裏,以饗讀者。java
一共寫了兩個版本,其編碼、解碼的時間複雜度均爲O(n),但版本二使用了一些技巧,使得其效率更高,編碼效率約是版本一的3倍,解碼效率約是版本一的4倍,同時編碼、解碼循環內少了一個判斷。算法
版本一:編程
- <html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
- <title>Base64編碼、解碼算法 - 夢遼軟件工做室</title>
- <style type="text/css">
- body,table {
- font-family:宋體;
- font-size:9pt;
- }
- input {
- width:200px;
- height:25px;
- }
- </style>
- </head>
- <body>
- <script type="text/javascript">
- /*
- Base64編碼規則:
- 一、將三個byte的數據,前後放入一個24bit的緩衝區中,先來的byte佔高位;
- 二、數據不足3byte的話,緩衝區中剩下的bit用0補足;
- 三、而後,每次取出6個bit(由於2^6=64,即0到63),按照其值選擇ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/中的字符做爲編碼後的輸出;
- 四、不斷進行,直到所有輸入數據轉換完成;
- 五、若是最後剩下兩個輸入數據,在編碼結果後加1個=;若是最後剩下一個輸入數據,編碼結果後加2個=;若是沒有剩下任何數據,則什麼都不加,這樣能夠保證數據還原的正確性。
- 注1:先對輸入字符串進行單字節編碼,不然,由於charCodeAt()對漢字等符號返回Unicode編碼,其長度爲16bit;所以,可將全部字符當作雙字節處理,雖然增長了字節數量,但簡化了雙字節字符和單字節字符的識別
- 注2:在JavaScript中,CJK ExtB(擴展字符平面2)中的字符均被當作兩個字符,用4Byte編碼,即字符"𠀀"~"𪛖",其編碼0xD840,0xDC00~0xD869~0xDED6,
- 例:語句:alert("𪛖".charCodeAt(0).toString(16)+" "+"𪛖".charCodeAt(1).toString(16));將顯示:d869 ded6
- */
- function unicodeToByte(str) //將Unicode字符串轉換爲UCS-16編碼的字節數組
- {
- var result=[];
- for(var i=0;i<str.length;i++)
- result.push(str.charCodeAt(i)>>8,str.charCodeAt(i)&0xff);
- return result;
- }
- function byteToUnicode(arr) //將UCS-16編碼的字節數組轉換爲Unicode字符串
- {
- var result="";
- for(var i=0;i<arr.length;i+=2)
- result+=String.fromCharCode((arr[i]<<8)+arr[i+1]);
- return result;
- }
- var map="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; //Base64從0到63的對應編碼字符集
- function encodeBase64(str)
- {
- var buffer=0,result="";
- var arr=unicodeToByte(str);
- for(var i=0;i<arr.length;i++)
- {
- buffer=(buffer<<8)+arr[i];
- if(i%3==2) //每3個字節處理1次
- {
- result+=map.charAt(buffer>>18)+map.charAt(buffer>>12&0x3f)+map.charAt(buffer>>6&0x3f)+map.charAt(buffer&0x3f);
- buffer=0;
- }
- } //3的整數倍的字節已處理完成,剩餘的字節仍存放於buffer中
- if(arr.length%3==1) //剩餘1個字節
- result+=map.charAt(buffer>>2)+map.charAt(buffer<<4&0x3f)+"==";
- else if(arr.length%3==2) //剩餘2個字節
- result+=map.charAt(buffer>>10)+map.charAt(buffer>>4&0x3f)+map.charAt(buffer<<2&0x3f)+"=";
- return result;
- }
- function decodeBase64(str)
- {
- //逆向映射數字索引和Base64編碼字符集(簡單Hash)
- var s="var base64={";
- for(var i=0;i<64;i++)
- s+="\""+map.charAt(i)+"\":"+i+",";
- s+="\"=\":0};"; //將"="字符對應的編碼定義爲0,免除額外的處理
- eval(s);
- var buffer=0,result=[];
- for(i=0;i<str.length;i++)
- {
- buffer=(buffer<<6)+base64[str.charAt(i)];
- if(i%4==3) //每3個Base64字符處理一次
- {
- result.push(buffer>>16,buffer>>8&0xff,buffer&0xff);
- buffer=0;
- }
- } //4的整數倍的Base64字符已處理完成,剩餘的Base64字符仍存放於buffer中
- if(/==$/g.test(str)) //剩餘2個Base64字符
- result.push(buffer>>4);
- else if(/=$/g.test(str)) //剩餘3個Base64字符,不可能剩餘1個Base64字符
- result.push(buffer>>10,buffer>>2&0xff);
- return byteToUnicode(result);
- }
- </script>
- <p>Base64編碼、解碼算法<br /><br />
- 白宇 - 夢遼軟件工做室 - 博訊網絡有限責任公司<br />
- 2011.05.30</p>
- <table border="0">
- <tr>
- <td>輸入:</td>
- <td>Base64編碼:</td>
- <td>Base64解碼:</td>
- </tr>
- <tr>
- <td>
- <textarea wrap="soft" id="input" cols="40" rows="30"></textarea>
- </td>
- <td>
- <textarea wrap="soft" id="encode" cols="40" rows="30"></textarea>
- </td>
- <td>
- <textarea wrap="soft" id="decode" cols="40" rows="30"></textarea>
- </td>
- </tr>
- <tr>
- <td align="center">
- <input type="button" value="編碼 →" onClick="encode.value=encodeBase64(input.value)" />
- </td>
- <td align="center">
- <input type="button" value="解碼 →" onClick="decode.value=decodeBase64(encode.value);" />
- </td>
- <td align="center">
- <input type="button" value="校驗 √" onClick="alert(input.value==decode.value?'校驗正確!':'校驗錯誤!');" />
- </td>
- </tr>
- </table>
- </body>
- </html>
版本二:數組
- <html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
- <title>Base64編碼、解碼算法(版本2) - 夢遼軟件工做室</title>
- <style type="text/css">
- body,table {
- font-family:宋體;
- font-size:9pt;
- }
- input {
- width:200px;
- height:25px;
- }
- </style>
- </head>
- <body>
- <script type="text/javascript">
- /*
- Base64編碼規則:
- 一、將三個byte的數據,前後放入一個24bit的緩衝區中,先來的byte佔高位;
- 二、數據不足3byte的話,緩衝區中剩下的bit用0補足;
- 三、而後,每次取出6個bit(由於2^6=64,即0到63),按照其值選擇ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/中的字符做爲編碼後的輸出;
- 四、不斷進行,直到所有輸入數據轉換完成;
- 五、若是最後剩下兩個輸入數據,在編碼結果後加1個=;若是最後剩下一個輸入數據,編碼結果後加2個=;若是沒有剩下任何數據,則什麼都不加,這樣能夠保證數據還原的正確性。
- 注1:先對輸入字符串進行單字節編碼,不然,由於charCodeAt()對漢字等符號返回Unicode編碼,其長度爲16bit;所以,可將全部字符當作雙字節處理,雖然增長了字節數量,但簡化了雙字節字符和單字節字符的識別
- 注2:在JavaScript中,CJK ExtB(擴展字符平面2)中的字符均被當作兩個字符,用4Byte編碼,即字符"𠀀"~"𪛖",其編碼0xD840,0xDC00~0xD869~0xDED6,
- 例:語句:alert("𪛖".charCodeAt(0).toString(16)+" "+"𪛖".charCodeAt(1).toString(16));將顯示:d869 ded6
- 技巧:編碼時處理源字節,若是字節總數模3餘1,則可如今其後面添加2個爲0的字節,若是模3餘2,則添加1個爲0的字節,而後在編碼完成後將末尾的2個或1個A字符均替換爲=字符;
- 解碼時一樣能夠將末尾的=字符替換爲A字符,因爲A字符對應0,而0解碼爲空字符,故可不作任何處理(編碼非字符類型的其它字節流,如圖片、音視頻等,則必須將末尾的0字節去除)。
- */
- function unicodeToByte(str) //將Unicode字符串轉換爲UCS-16編碼的字節數組
- {
- var result=[];
- for(var i=0;i<str.length;i++)
- result.push(str.charCodeAt(i)>>8,str.charCodeAt(i)&0xff);
- return result;
- }
- function byteToUnicode(arr) //將UCS-16編碼的字節數組轉換爲Unicode字符串
- {
- var result="";
- for(var i=0;i<arr.length;i+=2)
- result+=String.fromCharCode((arr[i]<<8)+arr[i+1]);
- return result;
- }
- var map="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; //Base64從0到63的對應編碼字符集
- function encodeBase64(str)
- {
- var buffer,result="",flag=0; //flag表示在字節數組剩餘的個數
- var arr=unicodeToByte(str);
- flag=arr.length%3;
- if(flag==1)
- arr.push(0,0);
- else if(flag==2)
- arr.push(0);
- for(var i=0;i<arr.length;i+=3) //此時arr.length必定能被3整除
- {
- buffer=(arr[i]<<16)+(arr[i+1]<<8)+arr[i+2];
- result+=map.charAt(buffer>>18)+map.charAt(buffer>>12&0x3f)+map.charAt(buffer>>6&0x3f)+map.charAt(buffer&0x3f);
- }
- if(flag==1)
- resultresult=result.replace(/AA$/g,"==");
- else if(flag==2)
- resultresult=result.replace(/A$/g,"=");
- return result;
- }
- function decodeBase64(str)
- {
- //逆向映射數字索引和Base64編碼字符集(簡單Hash)
- var s="var base64={";
- for(var i=0;i<64;i++)
- s+="\""+map.charAt(i)+"\":"+i+",";
- s+="\"=\":0};"; //將"="字符對應的編碼定義爲0,至關於將=字符轉換爲A字符
- eval(s);
- var buffer,result=[];
- for(i=0;i<str.length;i+=4) //因爲包含Base64末尾包含1個或2個=字符,故str.length必定能被4整除
- {
- buffer=(base64[str.charAt(i)]<<18)+(base64[str.charAt(i+1)]<<12)+(base64[str.charAt(i+2)]<<6)+base64[str.charAt(i+3)];
- result.push(buffer>>16,buffer>>8&0xff,buffer&0xff);
- }
- if(/==$/g.test(str)) //如解碼爲字符串可不作該處理
- {
- result.pop();
- result.pop();
- }
- else if(/=$/g.test(str))
- result.pop();
- return byteToUnicode(result);
- }
- </script>
- <p>Base64編碼、解碼算法(版本2)<br /><br />
- 白宇 - 夢遼軟件工做室 - 博訊網絡有限責任公司<br />
- 2011.05.31</p>
- <table border="0">
- <tr>
- <td>輸入:</td>
- <td>Base64編碼:</td>
- <td>Base64解碼:</td>
- </tr>
- <tr>
- <td>
- <textarea wrap="soft" id="input" cols="40" rows="30"></textarea>
- </td>
- <td>
- <textarea wrap="soft" id="encode" cols="40" rows="30"></textarea>
- </td>
- <td>
- <textarea wrap="soft" id="decode" cols="40" rows="30"></textarea>
- </td>
- </tr>
- <tr>
- <td align="center">
- <input type="button" value="編碼 →" onClick="encode.value=encodeBase64(input.value)" />
- </td>
- <td align="center">
- <input type="button" value="解碼 →" onClick="decode.value=decodeBase64(encode.value);" />
- </td>
- <td align="center">
- <input type="button" value="校驗 √" onClick="alert(input.value==decode.value?'校驗正確!':'校驗錯誤!');" />
- </td>
- </tr>
- </table>
- </body>
- </html>
這是完整的HTML文件(單文件),直接保存後就能夠運行了。網絡