偏要叫[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

山上层层桃李花,云间烟火是人家。
银钏金钗来负水,长刀短笠去烧畲。
唐代·刘禹锡《竹枝词九首·其九》

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