1. 一文搞懂Base64編碼原理

        共 8197字,需瀏覽 17分鐘

         ·

        2021-08-24 01:22

        Base64是最常見(jiàn)的用于傳輸8Bit字節(jié)碼的編碼方式之一,它是一種基于64個(gè)可打印字符來(lái)表示二進(jìn)制數(shù)據(jù)的方法。

        Base64是什么

        Base64編碼,是由64個(gè)字符組成編碼集:26個(gè)大寫(xiě)字母A~Z,26個(gè)小寫(xiě)字母a~z,10個(gè)數(shù)字0~9,符號(hào)“+”與符號(hào)“/”。Base64編碼的基本思路是將原始數(shù)據(jù)的三個(gè)字節(jié)拆分轉(zhuǎn)化為四個(gè)字節(jié),然后根據(jù)Base64的對(duì)應(yīng)表,得到對(duì)應(yīng)的編碼數(shù)據(jù)。

        當(dāng)原始數(shù)據(jù)湊不夠三個(gè)字節(jié)時(shí),編碼結(jié)果中會(huì)使用額外的符號(hào)“=”來(lái)表示這種情況。

        Base64編碼表

        碼值 字符 碼值 字符 碼值 字符
        0 A 26 a 52 0
        1 B 27 b 53 1
        2 C 28 c 54 2
        3 D 29 d 55 3
        4 E 30 e 56 4
        5 F 31 f 57 5
        6 G 32 g 58 6
        7 H 33 h 59 7
        8 I 34 i 60 8
        9 J 35 j 61 9
        10 K 36 k 62 +
        11 L 37 l 63 /
        12 M 38 m

        13 N 39 n

        14 O 40 o

        15 P 41 p

        16 Q 42 q

        17 R 43 r

        18 S 44 s

        19 T 45 t

        20 U 46 u

        21 V 47 v

        22 W 48 w

        23 X 49 x

        24 Y 50 y

        25 Z 51 z

        Base64編碼步驟

        • 將原始數(shù)據(jù)按照每三個(gè)字節(jié)作為一組進(jìn)行劃分,每組一共是24個(gè)二進(jìn)制位。
        • 再將這24個(gè)二進(jìn)制位,每6個(gè)一劃分,分為四組(6×4=24個(gè)二進(jìn)制位)。
        • 然后在每組前面補(bǔ)上00,擴(kuò)展成8×4=32個(gè)二進(jìn)制位,即四個(gè)字節(jié)(因?yàn)槊總€(gè)字節(jié)前面有2個(gè)0,所以每個(gè)字節(jié)的最大值是63)。
        • 最后根據(jù)Base64編碼表,將這四個(gè)字節(jié)的碼值,轉(zhuǎn)換為對(duì)應(yīng)的Base64的字符即可。

        Base64編碼過(guò)程舉例

        情況1:正常的3個(gè)字節(jié)編碼

        將單詞“PCB”轉(zhuǎn)換為Base64編碼:

        • "P"、"C"、"B"的ASCII值分別是80、67、66,對(duì)應(yīng)的二進(jìn)制值是0101 0000、0100 0011、0100 0010,將它們連成一個(gè)24位的二進(jìn)制字符串010100000100001101000010。

        • 將這個(gè)24位的二進(jìn)制字符串,每6個(gè)一組分成4組:010100、000100、001101、000010。

        • 在每組前面加兩個(gè)00,擴(kuò)展成32個(gè)二進(jìn)制位,即四個(gè)字節(jié):00010100、00000100、00001101、00000010。它們的十進(jìn)制值分別是19、22、5、46。(最前面加上兩個(gè)0只是為了湊成一個(gè)字節(jié),實(shí)際上其本身的數(shù)值是沒(méi)有變化的)

        • 根據(jù)上表,得到每個(gè)值對(duì)應(yīng)Base64編碼,即U、E、N、C。

        情況2:剩余2個(gè)字節(jié)編碼

        對(duì)于2個(gè)字節(jié)(16個(gè)二進(jìn)制數(shù))的情況,比如將“PC”轉(zhuǎn)換為Base64編碼:

        轉(zhuǎn)換方法同上,區(qū)別在于:

        • 16個(gè)二進(jìn)制數(shù),每6個(gè)一組分割,最后剩余4個(gè),這時(shí)再在后面補(bǔ)兩個(gè)0湊成6個(gè)。
        • 然后還按照基礎(chǔ)的方法轉(zhuǎn)換,最后補(bǔ)一個(gè)“=”即可

        轉(zhuǎn)換過(guò)程如下表,最終將“PC”轉(zhuǎn)換為了“UEM=”

        情況3:剩余1個(gè)字節(jié)編碼

        對(duì)于12個(gè)字節(jié)(8個(gè)二進(jìn)制數(shù))的情況,比如將“P”轉(zhuǎn)換為Base64編碼:

        轉(zhuǎn)換方法同上,區(qū)別在于:

        • 16個(gè)二進(jìn)制數(shù),每6個(gè)一組分割,最后剩余2個(gè),后面要再補(bǔ)4個(gè)0
        • 然后還按照基礎(chǔ)的方法轉(zhuǎn)換,最后補(bǔ)兩個(gè)“=”即可

        轉(zhuǎn)換過(guò)程如下表,最終將“P”轉(zhuǎn)換為了“UA==”



        Base64編解碼C程序

        編碼程序

        編碼的程序設(shè)計(jì)思路,就是按照上面講解的編碼過(guò)程,每3個(gè)原始字符為一組,進(jìn)行編碼,得到4個(gè)base64的字符。對(duì)于不夠3個(gè)字符的情況,編碼的base64的字符后面補(bǔ)上一到兩個(gè)=號(hào)。

        #include <stdio.h>
        #include <string.h>

        /*base64符號(hào)表*/
        const char *base64Arr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
        /*base64增補(bǔ)符號(hào)*/
        const char paddingChar = '=';

        /** @func: base64_encode
        * @brief: base64編碼
        * @para: [srcData]:要進(jìn)行編碼的原始數(shù)據(jù)
        * [resBase64]:base64編碼結(jié)果
        * @return:none
        */

        void base64_encode(const unsigned char * srcData, char * resBase64)
        {
        int i=0; /*原始數(shù)據(jù)索引*/
        int j=0; /*base64結(jié)果索引*/
        unsigned char transIdx=0; // 索引是8位,但是高兩位都為0
        const int srcLen = strlen((const char*)srcData);

        /*每3個(gè)一組,進(jìn)行編碼*/
        for(i=0; i < srcLen; i+=3)
        {
        /*取出第1個(gè)字符的高6位*/
        transIdx = ((srcData[i] >> 2) & 0x3f); /*0011 1111*/
        /*查表*/
        resBase64[j++] = base64Arr[(int)transIdx];

        /*取出第1個(gè)字符的低2位*/
        transIdx = ((srcData[i] << 4) & 0x30); /*0011 0000*/

        /*第1個(gè)字符后面還有字符*/
        if (i + 1 < srcLen)
        {
        /*取出第2個(gè)字符的高4位,并與第1個(gè)字符的低2位進(jìn)行組合*/
        transIdx |= ((srcData[i + 1] >> 4) & 0x0f); /*0000 1111*/
        /*查表*/
        resBase64[j++] = base64Arr[(int)transIdx];
        }
        else /*第1個(gè)字符后面沒(méi)有字符了*/
        {
        /*直接使用第1個(gè)字符的低2位查表*/
        resBase64[j++] = base64Arr[(int)transIdx];
        /*然后補(bǔ)上兩個(gè)=號(hào)*/
        resBase64[j++] = paddingChar;
        resBase64[j++] = paddingChar;
        break; /*沒(méi)有數(shù)據(jù)了,break結(jié)束*/
        }
        /*取出第2個(gè)字符的低4位*/
        transIdx = ((srcData[i + 1] << 2) & 0x3c); /*0011 1100*/
        /*第2個(gè)字符后面還有字符*/
        if (i + 2 < srcLen)
        {
        /*取出第3個(gè)字符的高2位,并與第2個(gè)字符的低4位進(jìn)行組合*/
        transIdx |= ((srcData[i + 2] >> 6) & 0x03); /*0000 0011*/
        /*查表*/
        resBase64[j++] = base64Arr[(int)transIdx];
        /*取出第3個(gè)字符的低6位*/
        transIdx = srcData[i + 2] & 0x3f; /*0011 1111*/
        /*查表*/
        resBase64[j++] = base64Arr[(int)transIdx];
        }
        else /*第2個(gè)字符后面沒(méi)有字符了*/
        {
        /*直接使用第2個(gè)字符的低4位查表*/
        resBase64[j++] = base64Arr[(int)transIdx];
        /*然后補(bǔ)上一個(gè)=號(hào)*/
        resBase64[j++] = paddingChar;
        break; /*沒(méi)有數(shù)據(jù)了,break結(jié)束*/
        }
        }

        /*結(jié)束符*/
        resBase64[j] = '\0';
        }

        解碼程序

        解碼的程序設(shè)計(jì)思路,其實(shí)就是編碼的反過(guò)程,把要解碼的base64符號(hào),每4個(gè)為一組,譯碼成3個(gè)字符。對(duì)于最后出現(xiàn)的=的情況,就說(shuō)明是要結(jié)束了,直接使用剩余的base64符號(hào)進(jìn)行譯碼,然后就結(jié)束了。

        /** @func:  idx_in_base64Arr
        * @brief: 在base64符號(hào)表中查找字符c對(duì)應(yīng)的索引值
        * @para: [c]:要查找的字符
        * @return:字符c在base64符號(hào)表中對(duì)應(yīng)的索引值(0~63)
        */

        int idx_in_base64Arr(char c)
        {
        /*在base64表中搜索第一次出現(xiàn)字符c的位置*/
        const char *pIdx = strchr(base64Arr, c);
        if (NULL == pIdx)
        {
        /*找不到對(duì)應(yīng)的base64字符,說(shuō)明輸入的base64字符串有誤*/
        return -1;
        }

        /*返回字符c在base64表中的位置*/
        return (pIdx - base64Arr);
        }

        /** @func: base64_decode
        * @brief: base64解碼
        * @para: [srcBase64]:要進(jìn)行解碼的原始base64數(shù)據(jù)
        * [resData]:解碼出的結(jié)果
        * @return:none
        */

        void base64_decode(const char *srcBase64, unsigned char *resData)
        {
        int i = 0; /*原始base64數(shù)據(jù)索引*/
        int j = 0; /*解碼后的結(jié)果數(shù)據(jù)索引*/
        int trans[4] = {0,0,0,0}; /*4個(gè)base64符號(hào)對(duì)應(yīng)的表中的位置(0~63的數(shù)字)轉(zhuǎn)換值*/

        /*base64符號(hào)每4個(gè)一組,譯碼成3個(gè)字符*/
        for (i=0; srcBase64[i]!='\0'; i+=4)
        {
        /*------譯碼第1個(gè)字符------*/
        /*前2個(gè)base64符號(hào)在表中的位置(0~63的數(shù)字)*/
        trans[0] = idx_in_base64Arr(srcBase64[i]);
        trans[1] = idx_in_base64Arr(srcBase64[i+1]);

        /*第1個(gè)符號(hào)的后6位,與第2個(gè)符號(hào)的6、5位,譯出第1個(gè)字符*/
        resData[j++] = ((trans[0] << 2) & 0xfc) | ((trans[1]>>4) & 0x03); /*1111 1100 0000 0011 */

        /*------譯碼第2個(gè)字符------*/
        /*第3個(gè)base64符號(hào)是否是=號(hào)*/
        if (srcBase64[i+2] != '=')
        {
        /*第3個(gè)base64符號(hào)在表中的位置(0~63的數(shù)字)*/
        trans[2] = idx_in_base64Arr(srcBase64[i + 2]);
        }
        else
        {
        break;/*沒(méi)有數(shù)據(jù)了,break結(jié)束*/
        }
        /*第2個(gè)符號(hào)的后4位,與第3個(gè)符號(hào)的6、5、4、3位,譯出第2個(gè)字符*/
        resData[j++] = ((trans[1] << 4) & 0xf0) | ((trans[2] >> 2) & 0x0f); /*1111 0000 0000 1111*/

        /*------譯碼第3個(gè)字符------*/
        /*第4個(gè)base64符號(hào)是否是=號(hào)*/
        if (srcBase64[i + 3] != '=')
        {
        /*第4個(gè)base64符號(hào)在表中的位置(0~63的數(shù)字)*/
        trans[3] = idx_in_base64Arr(srcBase64[i + 3]);
        }
        else
        {
        break;/*沒(méi)有數(shù)據(jù)了,break結(jié)束*/
        }
        /*第3個(gè)符號(hào)的后2位,與第4個(gè)符號(hào)的后6位,譯出第3個(gè)字符*/
        resData[j++] = ((trans[2] << 6) & 0xc0) | (trans[3] & 0x3f); /*1100 0000 0011 1111*/
        }

        /*結(jié)束符*/
        resData[j] = '\0';
        }

        測(cè)試程序

        使用字符串“PCB”進(jìn)行base64編碼測(cè)試,然后再將編碼得到的結(jié)果,進(jìn)行解碼測(cè)試。

        /*測(cè)試*/
        int main()
        {
        /*定義要進(jìn)行base64編碼的字符串*/
        const unsigned char *srcData = "PCB" ;

        /*先測(cè)試編碼*/
        char base64[128];
        base64_encode(srcData, base64);
        printf("base64編碼:%s\n",base64);

        /*再測(cè)試解碼*/
        char resData[128];
        base64_decode(base64, (unsigned char*)resData);
        printf("base64解碼:%s", resData);

        return 0;
        }

        輸出結(jié)果如下,可以看出,“PCB”進(jìn)行base64編碼,得到了“UENC”,然后再反向解碼,又得到了“PCB”

        base64編碼:UENC
        base64解碼:PCB
        --------------------------------
        Process exited after 0.01123 seconds with return value 0
        請(qǐng)按任意鍵繼續(xù). . .

        完整程序可從我的gitee倉(cāng)庫(kù)下載(點(diǎn)擊閱讀原文,直達(dá)代碼倉(cāng)庫(kù)~)


        瀏覽 65
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
          
          

            1. 淫淫婷婷 | 穿着情趣内衣被c高潮 | 性xxxxx视频 | 337p粉嫩大胆噜噜噜亚瑟影院 | 国产a一三三四区电影 |