[C++] Base64編碼

前言

  Base64非常常見的用於傳輸8Bit字節碼的編碼方式之一,也就是採用64個可打印的自體來編碼二進制數據進行傳輸的方法。在ASCII表中,0~31、127是控制字符32~126屬於可打印字符,而Http協議下傳輸二進制數據,是需要把數據轉爲字符來傳輸的,所以實際上能用於傳輸的就是ASCII表中的32~126這95個字符,base64則是採用了其中的64個來編碼。
  標準base64中可打印的64個字符爲26個字母的大小寫、10個數字以及+、/,共64個字符,但是由於在Http傳輸中URL編碼器會把標準base64中的"/“和”+“字符變爲形如“%XX”的形式,而且由於”%“是ANSI SQL中規定的通配符,這個時候又會對”%"進行轉換等操作,因此針對http協議的傳輸,可採用改進的URL安全的base64編碼,當然,除了URL安全的base64編碼方式,還有其他改進的方案(如正則表達式的base64等),但是好像標準base64和URL安全的base64比較常見。

標準base64編碼

  標準的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

  根據上邊的索引表,實際上,64字符只需要6位即可表示,但是ASCII表使用8位來表示的,所以在轉換的時候是採用了4*6個bit來存儲3*8bit數據,所以就會有如下轉換方式:
1. 當數據剛好是3的倍數的時候:

ASCII S O n
10進制 83 111 110
2進制 01010011 01101111 01101110
每6個bit一組 010100 110110 111101 101110
高位補0 00010100 00110110 00111101 00101110
base64索引 20 54 61 46
base64字符 U 2 9 u

2. 當數據不滿足3的倍數的時候:此時規定不足的一律補0

ASCII S
10進制 83
2進制 01010011
每6個bit一組 010100 110000 000000 000000
高位補0 00010100 00110000 00000000 00000000
base64索引 20 48
base64字符 U w = =

S的二進制前6位補零後爲字符“U”,第二組末尾補4個0轉換後爲字符“w”。剩下的使用“=”替代。即字符“S”通過Base64編碼後爲“Uw==”。這就是Base64的編碼過程。

C++實現標準base64編碼

  C++實現base64編碼是基於std::string操作來實現的,代碼主要來自github上開源的一個項目[1],其實現了基於std::string的base64編碼和解碼。
  編碼部分代碼如下:

std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) 
{
    std::string ret;
    int i = 0;
    int j = 0;
    unsigned char char_array_3[3];
    unsigned char char_array_4[4];

    while (in_len--)
    {
        char_array_3[i++] = *(bytes_to_encode++);
        if (i == 3) 
        {
          char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
          char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
          char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
          char_array_4[3] = char_array_3[2] & 0x3f;

          for(i = 0; (i <4) ; i++)
          {
              ret += base64_chars[char_array_4[i]];
          }
          i = 0;
        }
    }

    if (i)
    {
        for(j = i; j < 3; j++)
        {
            char_array_3[j] = '\0';
        }

        char_array_4[0] = ( char_array_3[0] & 0xfc) >> 2;
        char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
        char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);

        for (j = 0; (j < i + 1); j++)
        {
            ret += base64_chars[char_array_4[j]];
        }
        while((i++ < 3))
        {
            ret += '=';
        }
    }

    return ret;
}

  解碼部分代碼如下:

std::string base64_decode(std::string const& encoded_string) 
{
    size_t in_len = encoded_string.size();
    int i = 0;
    int j = 0;
    int in_ = 0;
    unsigned char char_array_4[4], char_array_3[3];
    std::string ret;

    while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_]))
    {
        char_array_4[i++] = encoded_string[in_]; in_++;
        if (i ==4)
        {
            for (i = 0; i <4; i++)
            {
                char_array_4[i] = base64_chars.find(char_array_4[i]) & 0xff;
            }

            char_array_3[0] = ( char_array_4[0] << 2       ) + ((char_array_4[1] & 0x30) >> 4);
            char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
            char_array_3[2] = ((char_array_4[2] & 0x3) << 6) +   char_array_4[3];

            for (i = 0; (i < 3); i++)
            {
                ret += char_array_3[i];
            }
            i = 0;
        }
    }

    if (i)
    {
        for (j = 0; j < i; j++)
        {
            char_array_4[j] = base64_chars.find(char_array_4[j]) & 0xff;
        }

        char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
        char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);

        for (j = 0; (j < i - 1); j++)
        {
            ret += char_array_3[j];
        }
    }

    return ret;
}

  由於要支持URL安全傳輸,所以我在這兩個函數之上又添加了URL安全的base64,主要添加的工作量很少,只是按照URL安全的base64的規則,對編碼後的+和/進行替換而已,也就是“+”和“/”分別改成了“-”和“_”,所以實現的代碼兩不多,代碼如下:

static inline char b64_to_safe(char c) 
{
    switch (c)
    {
        case '+':
            return '-';
            break;
        case '/':
            return '_';
            break;
        default:
            return c;
    }
}

static inline char safe_to_b64(char c) 
{
    switch (c) {
        case '-':
            return '+';
            break;
        case '_':
            return '/';
            break;
        default:
            return c;
    }
}

std::string urlsafe_base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len)
{
    std::string b64_encode = base64_encode(bytes_to_encode, in_len);
    std::string::iterator it;
    for(it = b64_encode.begin(); it != b64_encode.end(); it++)
    {
        *it = b64_to_safe(*it);
    }


    return b64_encode;

}

std::string urlsafe_base64_decode(std::string const& encoded_string)
{
    std::string b64_decode = encoded_string;
    std::string::iterator it;
    for(it = b64_decode.begin(); it != b64_decode.end(); it++)
    {
        *it = safe_to_b64(*it);
    }
    b64_decode = base64_decode(b64_decode);

    return b64_decode;
}

參考

[1] github https://github.com/drleq/CppBase64
[2] csdn https://blog.csdn.net/qq_20545367/article/details/79538530

山上層層桃李花,雲間煙火是人家。
銀釧金釵來負水,長刀短笠去燒畲。
唐代·劉禹錫《竹枝詞九首·其九》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章