關於Base64編碼

做者:唐風html

Base 64是一種比較古老的編碼方式,在通訊中很是常見。它實現很簡單。ui

What?

Base64是一種基於64個可打印字符來表示二進制數據的表示方法(來自維基)」。這句話我一開始沒有看懂,如今我用我懂的方式再解釋一下:咱們能夠把通訊的數據流分爲兩種,「二進制流」和「文本流」。(注意,後面的定義並不嚴謹)。文本流是指數據串是以「人類可讀的字符」組成的,數據流中出現的 0x00,0x0a,0x0d 等數據通常都是特殊的控制數據(文本結束,或是換行、或者是其它的),而不是數據自己。二進制流是任意的一串數據(每一個字節能夠是從 0x00 到 0xff 的值,而不限於字符)。Base64 編碼就是用可打印字符(A-Z,a-z,0-9,+/這64個「字符」)組成的「文本流」來表示任意的二進制流數據的一種編碼方法。也就是:任意的二進制流,經過base64編碼後會變成一串只由可見字符(A-Z,a-z,0-9,+/這64個「字符」)組成的文本數據流。編碼

完整的base64定義可見參考RFC 1421RFC 2045spa

Why?

Base64有什麼用?!base 64編碼後的數據流會比原數據流更長(是原數據流長度的4/3,緣由見How的部分),那爲何還要用這種方式進行編碼呢?(一開始我很不理解的地方)設計

緣由是:通訊設備的種類繁多,設計各異,好比有些設備只能處理/傳輸7bit的數據,有些設備的通訊模塊只能處理文本流,等等,爲了保證在這些設備之間仍然能無障礙地進行任何二進制數據(8bit爲單位)的通訊,就把這些數據從新編碼成只由可打印字符組成的文本流。base64中選取的可打印字符幾乎在任何設備上都支持(字符集採用US-ASCII標準)。另外還常會見到的一個場景是,要求數據從控制檯(或者其它輸入/輸出設備)輸入或是輸出,因爲這些輸入輸出設備只支持文本操做或顯示,因此也須要把二進制數據流在可打印字符集之間進行轉換(編解碼)。 htm

How?

Base64編碼其實很簡單。將二進制數據流每三個字符一組進行分組(24bit),分組後,將24bit分紅4個6bit數據,爲每6bit數據前加上2個bit的0,就會獲得4個8bit數據(變成了4byte),每一個byte的值都在0-63的範圍之間。而後按預先設定的轉換表,把這些值轉換成對應的可打印字符。blog

前文本中引用的維基百科內容說明得也比較清楚。ip

過程參考下圖:ci

image

這裏有一個小細節須要注意,那就是若是要編碼的數據串長度不是 3 的整數倍的狀況下,須要在數據串後面補充若干(假設是N)個0x00使得數據長度剛好爲 3 的整數倍。而後再進行處理。獲得相應的base64字符流數據後,再把串最後面的N個A換成=號。get

Base64的解碼過程只須要反過來就能夠了。

下面是C++的代碼實現

Base 64 encoding and decoding
vector < char > Bin2Base64( vector < uint8_t > const & _bin ) {
     static char const convert_table[] = "ABCDEFGHIGKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" ;

     vector < char > output;
     output.reserve(( _bin .size() * 4) / 3);

     auto con_3bytes_to_4bytes = [&]( uint8_t const * _3bytes_bin_data ) {
         output.push_back(convert_table[ _3bytes_bin_data [0] >> 2]);
         output.push_back(convert_table[(( _3bytes_bin_data [0] & 0x03) << 4) | (( _3bytes_bin_data [1] >> 4) & 0x0f)]);
         output.push_back(convert_table[(( _3bytes_bin_data [1] << 2) & 0x3c) | ( _3bytes_bin_data [2] >> 6)]);
         output.push_back(convert_table[ _3bytes_bin_data [2] & 0x3f]);
     };

     auto i = 0u;
     for (; (i + 3) <= _bin .size(); i += 3) {
         con_3bytes_to_4bytes(& _bin [i]);
     }

     if (i != _bin .size()) {
         uint8_t left_data[3] = { 0 };
         std::copy(begin( _bin ) + i, end( _bin ), left_data);
         con_3bytes_to_4bytes(left_data);
         std::fill(rbegin(output), rbegin(output) + (3 - ( _bin .size() % 3)), '=' );
     }    

     return output;
}

vector < uint8_t > Base64ToBin( vector < char > const & _base64stream ) {
     vector < uint8_t > output;
     output.reserve( _base64stream .size() * 3 / 4);

     static hash_map < char , uint8_t > convert_table = {
             { 'A' , 0  }, { 'B' , 1  }, { 'C' , 2  }, { 'D' , 3  }, { 'E' , 4  }, { 'F' , 5  }, { 'G' , 6  }, { 'H' , 7  },
             { 'I' , 8  }, { 'J' , 9  }, { 'K' , 10 }, { 'L' , 11 }, { 'M' , 12 }, { 'N' , 13 }, { 'O' , 14 }, { 'P' , 15 },
             { 'Q' , 16 }, { 'R' , 17 }, { 'S' , 18 }, { 'T' , 19 }, { 'U' , 20 }, { 'V' , 21 }, { 'W' , 22 }, { 'X' , 23 },
             { 'Y' , 24 }, { 'Z' , 25 }, { 'a' , 26 }, { 'b' , 27 }, { 'c' , 28 }, { 'd' , 29 }, { 'e' , 30 }, { 'f' , 31 },
             { 'g' , 32 }, { 'h' , 33 }, { 'i' , 34 }, { 'j' , 35 }, { 'k' , 36 }, { 'l' , 37 }, { 'm' , 38 }, { 'n' , 39 },
             { 'o' , 40 }, { 'p' , 41 }, { 'q' , 42 }, { 'r' , 43 }, { 's' , 44 }, { 't' , 45 }, { 'u' , 46 }, { 'v' , 47 },
             { 'w' , 48 }, { 'x' , 49 }, { 'y' , 50 }, { 'z' , 51 }, { '0' , 52 }, { '1' , 53 }, { '2' , 54 }, { '3' , 55 },
             { '4' , 56 }, { '5' , 57 }, { '6' , 58 }, { '7' , 59 }, { '8' , 60 }, { '9' , 61 }, { '+' , 62 }, { '/' , 63 },
             { '=' , 0 },
     };

     auto i = 0u;
     for ( ; i < _base64stream .size(); i += 4) {
         auto const byte1 = convert_table[ _base64stream [i]];
         auto const byte2 = convert_table[ _base64stream [i + 1]];
         auto const byte3 = convert_table[ _base64stream [i + 2]];
         auto const byte4 = convert_table[ _base64stream [i + 3]];

         output.push_back((byte1 << 2) | (byte2 >> 4));
         output.push_back(((byte2 & 0x0f) << 4) | (byte3 >> 2));
         output.push_back(((byte3 & 0x03) << 6) | byte4);
     }

     output.erase(end(output) - count(rbegin( _base64stream ), rbegin( _base64stream ) + 4, '=' ),
                  end(output));

     return output;
}
相關文章
相關標籤/搜索