C语言应用(3)——Base64编码/解码
一、簡介
Base64是網(wǎng)絡(luò)上最常見的用于傳輸8Bit字節(jié)碼的編碼方式之一,Base64就是一種基于64個可打印字符來表示二進(jìn)制數(shù)據(jù)的方法。可查看RFC2045~RFC2049,上面有MIME的詳細(xì)規(guī)范。
Base64編碼是從二進(jìn)制到字符的過程,可用于在HTTP環(huán)境下傳遞較長的標(biāo)識信息。采用Base64編碼具有不可讀性,需要解碼后才能閱讀。
Base64由于以上優(yōu)點被廣泛應(yīng)用于計算機的各個領(lǐng)域,然而由于輸出內(nèi)容中包括兩個以上“符號類”字符(+, /, =),不同的應(yīng)用場景又分別研制了Base64的各種“變種”。為統(tǒng)一和規(guī)范化Base64的輸出,Base62x被視為無符號化的改進(jìn)版本。
二、原理
這里的討論的前提是使用 UTF-8 編碼
將輸入流中的字節(jié)按每3個8bit分為一組,然后每次取6個bit,將其轉(zhuǎn)換成表格中對應(yīng)的數(shù)據(jù),如此3個字節(jié)可Base64編碼為4個字符,字節(jié)數(shù)增加三分之一。一直重復(fù)到?jīng)]有剩余的字符為止,當(dāng)位數(shù)不夠時補0來滿足一個字符,且要實現(xiàn)編碼后的位數(shù)為4的倍數(shù),不足的用“=”代替。
- Base64編碼索引如下:
| 0 | A | 17 | R | 34 | i | 51 | z |
| 1 | B | 18 | S | 35 | j | 52 | 0 |
| 2 | C | 19 | T | 36 | k | 53 | 1 |
| 3 | D | 20 | U | 37 | l | 54 | 2 |
| 4 | E | 21 | V | 38 | m | 55 | 3 |
| 5 | F | 22 | W | 39 | n | 56 | 4 |
| 6 | G | 23 | X | 40 | o | 57 | 5 |
| 7 | H | 24 | Y | 41 | p | 58 | 6 |
| 8 | I | 25 | Z | 42 | q | 59 | 7 |
| 9 | J | 26 | a | 43 | r | 60 | 8 |
| 10 | K | 27 | b | 44 | s | 61 | 9 |
| 11 | L | 28 | c | 45 | t | 62 | + |
| 12 | M | 29 | d | 46 | u | 63 | / |
| 13 | N | 30 | e | 47 | v | ||
| 14 | O | 31 | f | 48 | w | ||
| 15 | P | 32 | g | 49 | x | ||
| 16 | Q | 33 | h | 50 | y |
三、示例
3.1 編碼
3.1.1 位數(shù)充足情況
- 第一步:“S”、“.”、"H"轉(zhuǎn)換為對應(yīng)的ASCII碼值分別為0x53、0x2e、0x48。
- 第二步:轉(zhuǎn)換為對應(yīng)的二進(jìn)制值是01010011、00101110、01001000。由此組成一個24位的二進(jìn)制字符串,這里不足8個bit的高位補0即可。
- 第三步:將24位每6個bit分為一組,共四組。分別為 010100、110010、111001、001000。
- 第四步:將其轉(zhuǎn)換成十進(jìn)制得到,20、50、57、8。
- 第五步:用上面的值在Base64編碼表中進(jìn)行查找,分別對應(yīng):U、y、5、I。因此“S.H”Base64編碼之后就變?yōu)?#xff1a;Uy5I。
3.1.2 位數(shù)不足情況
只有兩字符:
- 第一步:“S”、"H"轉(zhuǎn)換為對應(yīng)的ASCII碼值分別為0x53、0x48。
- 第二步:轉(zhuǎn)換為對應(yīng)的二進(jìn)制值是01010011、01001000。
- 第三步:將每6個bit分為一組,共三組。分別為 010100、110100、1000。可以看到最后一組的比特位不足 6 個,在這種情況下,會進(jìn)行末尾(低位)補0的操作。補完之后就會變成010100、110100、100000。但是你會發(fā)現(xiàn),這里總共也只有18個比特,不滿足 3 個字節(jié)一組的原則。在這種情況下,前三組會按照常規(guī)的Base64進(jìn)行編碼,而缺失的一組則會使用 = 來進(jìn)行填充。
- 第四步:將其轉(zhuǎn)換成十進(jìn)制得到,20、52、32。
- 第五步:用上面的值在Base64編碼表中進(jìn)行查找,分別對應(yīng):U、0、g,再加上最后填充的 =。因此“SH”Base64編碼之后就變?yōu)?#xff1a;U0g=。
只有一字符:
- 第一步:“S”轉(zhuǎn)換為對應(yīng)的ASCII碼值分別為0x53。
- 第二步:轉(zhuǎn)換為對應(yīng)的二進(jìn)制值是01010011。
- 第三步:將每6個bit分為一組,共兩組。分別為 010100、11。可以第二組明顯不滿 6 個比特,進(jìn)行低位補0操作。補完之后就會變成010100、110000。但是你會發(fā)現(xiàn),這里這里只有2組,不滿四組,所以這里需要填充2個 =。
- 第四步:將其轉(zhuǎn)換成十進(jìn)制得到,20、48。
- 第五步:用上面的值在Base64編碼表中進(jìn)行查找,分別對應(yīng):U、w,再加上最后填充的2個 =。因此“S”Base64編碼之后就變?yōu)?#xff1a;Uw==。
3.2 解碼
因為最終的編碼產(chǎn)物中,如果 6 個比特的分組不滿 4 組,會有 = 作為填充物,所以一個 Base64 完后的產(chǎn)物總是能夠被 4 整除。
所以,在解密中,我們每次需要處理 4 個字符,將這 4 個字符編碼之后轉(zhuǎn)換成十進(jìn)制,再轉(zhuǎn)換成二進(jìn)制,不足 6 位的高位補0,然后將 6 個比特一組的二進(jìn)制數(shù)按原順序重新分成每 8 個比特一組,也就是一個字節(jié)一組。然后將其轉(zhuǎn)換成十六進(jìn)制,再轉(zhuǎn)換成對應(yīng)的字符。
3.2.1 位數(shù)充足情況
- 第一步:按照每次處理4個字符的原理,Uy5I 在編碼索引表中的值分別為20,50,57,8。
- 第二步:將其轉(zhuǎn)換成二進(jìn)制,不足六位的高位補0,再將其分成每8個bit一組,分別為01010011、00101110、01111001。
- 第三步:將分組好的比特轉(zhuǎn)換成十六進(jìn)制,得到0x53、0x2e、0x48。
- 第四步:將十六進(jìn)制轉(zhuǎn)換成字母得到,“S”、“.”、“H”,也就是 “S.H”
3.2.2 位數(shù)不足情況
- 第一步:U0g= 在編碼索引表中的值分別為20,50,32。
- 第二步:將其轉(zhuǎn)換成二進(jìn)制,不足六位的高位補0,分別為010100、110100、100000。
- 第三步:再將其分成每8個bit一組,分別為01010011、01001000。
- 第四步:將分組好的比特轉(zhuǎn)換成十六進(jìn)制,得到0x53、0x48。
- 第五步:將十六進(jìn)制轉(zhuǎn)換成字母得到,“S”、“H”,也就是 “SH”
四、實現(xiàn)
4.1 base64.h
#ifndef _BASE64_H_ #define _BASE64_H_/********************************************************************** INCLUDES*/ #include <stdint.h>/********************************************************************** API FUNCTIONS*/ uint8_t Base64_Encode(char *pInData, uint32_t inLen, char *pOutData, uint32_t *pOutLen); uint8_t Base64_Decode(char *pInData, uint32_t inLen, char *pOutData, uint32_t *pOutLen);#endif /* _BASE64_H_ */4.2 base64.c
/********************************************************************** INCLUDES*/ #include <stdint.h> #include <stddef.h> #include "base64.h"/********************************************************************** LOCAL VARIABLES*/ static char s_base64Table[] = {'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4','5','6','7','8','9','+', '/', '\0' };/********************************************************************** PUBLIC FUNCTIONS*/ /**@brief Base64編碼@param pInData -[in] 源字符串@param inLen -[in] 源字符串長度@param pOutData -[out] 編碼后字符串@param pOutLen -[out] 解碼后字符串長度@return 1 - 成功;0 - 失敗 */ uint8_t Base64_Encode(char *pInData, uint32_t inLen, char *pOutData, uint32_t *pOutLen) {if(NULL == pInData || 0 == inLen){return 0;}uint32_t i = 0;uint32_t j = 0;uint32_t temp = 0;// 3字節(jié)一組進(jìn)行轉(zhuǎn)換for(i = 0; i < inLen; i += 3) {// 獲取第一個6位temp = (*(pInData + i) >> 2) & 0x3F;*(pOutData + j++) = s_base64Table[temp];// 獲取第二個6位的前兩位temp = (*(pInData + i) << 4) & 0x30;// 如果只有一個字符,那么需要做特殊處理if(inLen <= (i + 1)) {*(pOutData + j++) = s_base64Table[temp];*(pOutData + j++) = '=';*(pOutData + j++) = '=';break;}// 獲取第二個6位的后四位temp |= (*(pInData + i + 1) >> 4) & 0x0F;*(pOutData + j++) = s_base64Table[temp];// 獲取第三個6位的前四位temp = (*(pInData+ i + 1) << 2) & 0x3C;if(inLen <= (i + 2)){*(pOutData + j++) = s_base64Table[temp];*(pOutData + j++) = '=';break;}// 獲取第三個6位的后兩位temp |= (*(pInData + i + 2) >> 6) & 0x03;*(pOutData + j++) = s_base64Table[temp];// 獲取第四個6位temp = *(pInData + i + 2) & 0x3F;*(pOutData + j++) = s_base64Table[temp];}*(pOutData + j) = '\0';// 編碼后的長度*pOutLen = inLen * 8 / 6;return 1; }/**@brief Base64解碼@param pInData -[in] 源字符串@param inLen -[in] 源字符串長度@param pOutData -[out] 解碼后字符串@param pOutLen -[out] 解碼后字符串長度@return 1 - 成功;0 - 失敗 */ uint8_t Base64_Decode(char *pInData, uint32_t inLen, char *pOutData, uint32_t *pOutLen) {if(NULL == pInData || 0 == inLen || inLen % 4 != 0){return 0;}uint32_t i = 0;uint32_t j = 0;uint32_t k = 0;char temp[4] = "";// 4字節(jié)一組進(jìn)行轉(zhuǎn)換for(i = 0; i < inLen; i += 4){// 找到在編碼索引表中對應(yīng)的值for(j = 0; j < 64; j++) {if(*(pInData + i) == s_base64Table[j]){temp[0] = j;}} for(j = 0; j < 64; j++){if(*(pInData + i + 1) == s_base64Table[j]){temp[1] = j;}}for(j = 0; j < 64; j++){if(*(pInData + i + 2) == s_base64Table[j]){temp[2] = j;}}for(j = 0; j < 64; j++) {if(*(pInData + i + 3) == s_base64Table[j]) {temp[3] = j;}}// 獲取第一個6位和第二個6位的前兩位組成一個8位*(pOutData + k++) = ((temp[0] << 2) & 0xFC) | ((temp[1] >> 4) & 0x03);if(*(pInData + i + 2) == '='){break;}// 獲取第二個6位的前四位和第三個6位的前四位組成一個8位*(pOutData + k++) = ((temp[1] << 4) & 0xF0) | ((temp[2] >> 2) & 0x0F);if(*(pInData + i + 3) == '='){break;}// 獲取第三個6位的后兩位和第四個6位組成一個8位*(pOutData + k++) = ((temp[2] << 6) & 0xF0) | (temp[3] & 0x3F);}*pOutLen = k;return 1; }/****************************************************END OF FILE****************************************************/五、用法
- 編碼:
編碼前:a123456
編碼后:YTEyMzQ1Ng==
- 解碼:
解碼前:YTEyMzQ1Ng==
解碼后:a123456
? 由 Leung 寫于 2022 年 10 月 12 日
? 參考:Base64 原理
C語言base64實現(xiàn)
base64編碼原理及C語言實現(xiàn)
總結(jié)
以上是生活随笔為你收集整理的C语言应用(3)——Base64编码/解码的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 4G物联网网关可接入ONVIF/RTSP
- 下一篇: matlab制作圆摆线动画