服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C/C++ - VC中BASE64编码和解码使用详解

VC中BASE64编码和解码使用详解

2021-03-16 14:28C语言教程网 C/C++

Base64是一种很常用的编码方式,利用它可以将任何二进制的字符编码到可打印的64个字符之中, 这样,不管是图片,中文文本等都可以编码成只有ASCII的纯文本。

BASE64可以用来将binary的字节序列数据编码成ASCII字符序列构成的文本。完整的BASE64定义可见 RFC1421和 RFC2045。编码后的数据比原始数据略长,为原来的4/3。在电子邮件中,根据RFC822规定,每76个字符,还需要加上一个回车换行。

转换的时候,将三个byte的数据,先后放入一个24bit的缓冲区中,先来的byte占高位。数据不足3byte的话,于缓冲区中剩下的Bit用0补足。然后,每次取出6个bit,按照其值选择ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/中的字符作为编码后的输出。不断进行,直到全部输入数据转换完成。如果最后剩下两个输入数据,在编码结果后加1个“=”;如果最后剩下一个输入数据,编码结果后加2个“=”;如果没有剩下任何数据,就什么都不要加,这样才可以保证资料还原的正确性。

BASE64_API.h  文件内容

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
/* ----------------------------------------------------------
文件名称:BASE64_API.h
 
作者:秦建辉
 
MSN:splashcn@msn.com
 
当前版本:V1.1
 
历史版本:
  V1.1  2010年05月11日
      修正BASE64解码的Bug。
 
  V1.0  2010年05月07日
      完成正式版本。
 
功能描述:
  BASE64编码和解码
 
接口函数:
  Base64_Encode
  Base64_Decode
 
说明:
  1.  参考openssl-1.0.0。
  2.  改进接口,以使其适应TCHAR字符串。
  3.  修正EVP_DecodeBlock函数解码时未去掉填充字节的缺陷。
 ------------------------------------------------------------ */
#pragma once
#include "stdafx.h"
 
#include <windows.h>
 
#ifdef  __cplusplus
extern "C" {
#endif
 
/*
功能:将二进制数据转换成BASE64编码字符串
参数说明:
  inputBuffer:要编码的二进制数据
  inputCount:数据长度
  outputBuffer:存储转换后的BASE64编码字符串
返回值:
   -1:参数错误
  >=0:有效编码长度(字符数),不包括字符串结束符。
备注:
  等效于openssl中EVP_EncodeBlock函数
*/
INT BASE64_Encode( const BYTE* inputBuffer, INT inputCount, TCHAR* outputBuffer );
 
/*
功能:将BASE64编码字符串转换为二进制数据
参数说明:
  inputBuffer:BASE64编码字符串
  inputCount:编码长度(字符数),应该为4的倍数。
  outputBuffer:存储转换后的二进制数据
返回值:
   -1:参数错误
   -2:数据错误
  >=0:转换后的字节数
备注:
  等效于openssl中EVP_DecodeBlock函数
*/
INT BASE64_Decode( const TCHAR* inputBuffer, INT inputCount, BYTE* outputBuffer );
 
#ifdef __cplusplus
}
#endif

 

BASE64_API.cpp 文件内容

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
#pragma once
 
#include "stdafx.h"
 
#include "BASE64_API.h"
 
static const CHAR* DATA_BIN2ASCII = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 
INT BASE64_Encode( const BYTE* inputBuffer, INT inputCount, TCHAR* outputBuffer )
{
  INT i;
  BYTE b0, b1, b2;
 
  if( (inputBuffer == NULL) || (inputCount < 0) )
  {
    return -1;  // 参数错误
  }
 
  if( outputBuffer != NULL )
  {
    for( i = inputCount; i > 0; i -= 3 )
    {
      if( i >= 3 )
      // 将3字节数据转换成4个ASCII字符
        b0 = *inputBuffer++;
        b1 = *inputBuffer++;
        b2 = *inputBuffer++;
 
        *outputBuffer++ = DATA_BIN2ASCII[b0 >> 2];
        *outputBuffer++ = DATA_BIN2ASCII[((b0 << 4) | (b1 >> 4)) & 0x3F];
        *outputBuffer++ = DATA_BIN2ASCII[((b1 << 2) | (b2 >> 6)) & 0x3F];
        *outputBuffer++ = DATA_BIN2ASCII[b2 & 0x3F];
      }
      else
      {
        b0 = *inputBuffer++;
        if( i == 2 )b1 = *inputBuffer++; else b1 = 0;
 
        *outputBuffer++ = DATA_BIN2ASCII[b0 >> 2];
        *outputBuffer++ = DATA_BIN2ASCII[((b0 << 4) | (b1 >> 4)) & 0x3F];
        *outputBuffer++ = (i == 1) ? TEXT('=') : DATA_BIN2ASCII[(b1 << 2) & 0x3F];
        *outputBuffer++ = TEXT('=');
      }
    } // End for i
 
    *outputBuffer++ = TEXT('/0');  // 添加字符串结束标记
  }
 
  return ((inputCount + 2) / 3) * 4;  // 返回有效字符个数
}
 
#define B64_EOLN      0xF0  // 换行/n
#define B64_CR        0xF1  // 回车/r
#define B64_EOF        0xF2  // 连字符-
#define B64_WS        0xE0  // 跳格或者空格(/t、space)
#define B64_ERROR   0xFF  // 错误字符
#define B64_NOT_BASE64(a)  (((a)|0x13) == 0xF3)
 
static const BYTE DATA_ASCII2BIN[128] = {
  0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xE0,0xF0,0xFF,0xFF,0xF1,0xFF,0xFF,
  0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
  0xE0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x3E,0xFF,0xF2,0xFF,0x3F,
  0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,
  0xFF,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,
  0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0xFF,0xFF,0xFF,0xFF,0xFF,
  0xFF,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,
  0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,0x30,0x31,0x32,0x33,0xFF,0xFF,0xFF,0xFF,0xFF
};
 
INT BASE64_Decode( const TCHAR* inputBuffer, INT inputCount, BYTE* outputBuffer )
{
  INT i, j;
  BYTE b[4];
  TCHAR ch;
 
  if( (inputBuffer == NULL) || (inputCount < 0) )
  {
    return -1;  // 参数错误
  }
 
  // 去除头部空白字符
  while( inputCount > 0 )
  {
    ch = *inputBuffer;
    if( (ch < 0) || (ch >= 0x80) )
    {
      return -2;  // 数据错误,不在ASCII字符编码范围内
    }
    else
    {
      if( DATA_ASCII2BIN[ch] == B64_WS )
      {
        inputBuffer++;
        inputCount--;
      }
      else
      {
        break;
      }
    }
  }
 
  // 去除尾部的空白字符、回车换行字符、连字符
  while( inputCount >= 4 )
  {
    ch = inputBuffer[inputCount - 1];
    if( (ch < 0) || (ch >= 0x80) )
    {
      return -2;  // 数据错误,不在ASCII字符编码范围内
    }
    else
    {
      if( B64_NOT_BASE64(DATA_ASCII2BIN[ch]) )
      {
        inputCount--;
      }
      else
      {
        break;
      }
    }
  }
 
  // 字符串长度必须为4的倍数
  if( (inputCount % 4) != 0 )
  {
    return -2;  // 数据错误
  }
 
  if( outputBuffer != NULL )
  {
    for( i = 0; i < inputCount; i += 4 )
    {
      for( j = 0; j < 4; j++ )
      {
        ch = *inputBuffer++;
        if( (ch < 0) || (ch >= 0x80) )
        {
          return -2;  // 数据错误,不在ASCII字符编码范围内
        }
        else
        {
          if( ch == '=' // 发现BASE64编码中的填充字符
          {
            break;
          }
          else
          {
            b[j] = DATA_ASCII2BIN[ch];
            if( b[j] & 0x80 )
            {
              return -2;  // 数据错误,无效的Base64编码字符
            }
          }         
        }
      } // End for j
 
      if( j == 4 )
      {
        *outputBuffer++ = (b[0] << 2) | (b[1] >> 4);
        *outputBuffer++ = (b[1] << 4) | (b[2] >> 2 );
        *outputBuffer++ = (b[2] << 6) | b[3];
      }
      else if( j == 3 )
      // 有1个填充字节
        *outputBuffer++ = (b[0] << 2) | (b[1] >> 4);
        *outputBuffer++ = (b[1] << 4) | (b[2] >> 2 );
 
        return (i >> 2) * 3 + 2;
      }
      else if( j == 2 )
      // 有2个填充字节
        *outputBuffer++ = (b[0] << 2) | (b[1] >> 4);
 
        return (i >> 2) * 3 + 1;
      }
      else
      {
        return -2;  // 数据错误,无效的Base64编码字符
      }     
    // End for i
  }
 
  return (inputCount >> 2) * 3;
}

 

采用以上方法就可以将二进制数据转换成可见字符进行传递就可以了.

那么如何使用呢?举以下两个例子

第一个:将一个图片转换成 txt 文本 并保存起来

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//选择一个图像文件,将它转为 文本保存至 _T("D:\\2.txt"
void CTextPicDlg::OnBnClickedButton2()
{
  // TODO: 在此添加控件通知处理程序代码
  CFileDialog file(TRUE,".jpg","");
  if (file.DoModal() == IDOK)
  {
    CFile data(file.GetPathName(), CFile::modeReadWrite);
    int len = data.GetLength();
    BYTE *dv;
    dv = (BYTE *)malloc(len*sizeof(BYTE));
    data.Read(dv, len);
    data.Close();
    int slen = (len / 3) * 4;
    slen += 10;
    TCHAR * tc;
    tc = (TCHAR *)malloc(slen);
    slen = BASE64_Encode(dv, len, tc);
    CFile save(_T("D:\\2.txt"), CFile::modeCreate | CFile::modeWrite);
    save.Write(tc, slen);
    save.Close();
    free(tc);
    free(dv);
  }
}

 

第二个例子,将一个文本文件还原为一个图像

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
void CTextPicDlg::OnBnClickedButton3()
{
  // TODO: 在此添加控件通知处理程序代码
  CFileDialog file(TRUE, ".txt", "");
  if (file.DoModal() == IDOK)
  {
    CFile data(file.GetPathName(), CFile::modeReadWrite);
    int len = data.GetLength();
    TCHAR *dv;
    dv = (TCHAR *)malloc(len*sizeof(TCHAR));
    data.Read(dv, len);
    data.Close();
    int slen = (len / 4) * 3;
    slen += 10;
    BYTE * tc;
    tc = (BYTE *)malloc(slen);
    BASE64_Decode(dv, len, tc);
    //直接在内存里面构建CIMAGE,需要使用IStream接口,如何使用
    //构建内存环境   
    HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, slen);
    void * pData = GlobalLock(hGlobal);
    memcpy(pData, tc, slen); // 拷贝位图数据进去
    GlobalUnlock(hGlobal);
    // 创建IStream
    IStream * pStream = NULL;
    if (CreateStreamOnHGlobal(hGlobal, TRUE, &pStream) != S_OK)
      return ;
    //  使用CImage加载位图内存
    CImage img;
    if (SUCCEEDED(img.Load(pStream)) )
    {
      CClientDC dc(this);
       //使用内在中构造的图像 直接在对话框上绘图
       img.Draw(dc.m_hDC, 0, 0, 500, 300);
    }
     //释放内存
    pStream->Release();
    GlobalFree(hGlobal);
    //如果要保存图像文件的话,那就使用下面的代码
    //CFileDialog savefile(FALSE, ".jpg", "");
    //if (savefile.DoModal()==IDOK)
    //{
    //  CFile save(savefile.GetPathName(), CFile::modeCreate | CFile::modeWrite);
    //  save.Write(tc, slen);
    //  save.Close();
    //}
    free(tc);
    free(dv);
  }
}

至此,利用Base64转码的方式,来显示保存显示图片的方法,就算是成功了!

我们再来看一个base64编码解码的例子

首先是编码

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
const BYTE Base64ValTab[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 
#define AVal(x) Base64ValTab[x]
int CSeeBase64Dlg::EncodeBase64(char * pInput, char * pOutput)
{
    int i = 0;
    int loop = 0;
    int remain = 0;
    int iDstLen = 0;
    int iSrcLen = (int)strlen(pInput);
 
    loop = iSrcLen/3;
    remain = iSrcLen%3;
 
    // also can encode native char one by one as decode method
    // but because all of char in native string is to be encoded so encode 3-chars one time is easier.
 
    for (i=0; i < loop; i++)
    {
        BYTE a1 = (pInput[i*3] >> 2);
        BYTE a2 = ( ((pInput[i*3] & 0x03) << 4) | (pInput[i*3+1] >> 4) );
        BYTE a3 = ( ((pInput[i*3+1] & 0x0F) << 2) | ((pInput[i*3+2] & 0xC0) >> 6) );
        BYTE a4 = (pInput[i*3+2] & 0x3F);
 
        pOutput[i*4] = AVal(a1);
        pOutput[i*4+1] = AVal(a2);
        pOutput[i*4+2] = AVal(a3);
        pOutput[i*4+3] = AVal(a4);
    }
 
    iDstLen = i*4;
 
    if (remain == 1)
    {
        // should pad two equal sign
        i = iSrcLen-1;
        BYTE a1 = (pInput[i] >> 2);
        BYTE a2 = ((pInput[i] & 0x03) << 4);
        
        pOutput[iDstLen++] = AVal(a1);
        pOutput[iDstLen++] = AVal(a2);
        pOutput[iDstLen++] = '=';
        pOutput[iDstLen++] = '=';
        pOutput[iDstLen] = 0x00;
    }
    else if (remain == 2)
    {
        // should pad one equal sign
        i = iSrcLen-2;
        BYTE a1 = (pInput[i] >> 2);
        BYTE a2 = ( ((pInput[i] & 0x03) << 4) | (pInput[i+1] >> 4));
        BYTE a3 = ( (pInput[i+1] & 0x0F) << 2);
 
        pOutput[iDstLen++] = AVal(a1);
        pOutput[iDstLen++] = AVal(a2);
        pOutput[iDstLen++] = AVal(a3);
        pOutput[iDstLen++] = '=';
        pOutput[iDstLen] = 0x00;
    }
    else
    {
        // just division by 3
        pOutput[iDstLen] = 0x00;
    }
 
    return iDstLen;
}

下面是解码

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
const BYTE Base64IdxTab[128] =
{
255,255,255,255, 255,255,255,255, 255,255,255,255, 255,255,255,255,
255,255,255,255, 255,255,255,255, 255,255,255,255, 255,255,255,255,
255,255,255,255, 255,255,255,255, 255,255,255,62,  255,255,255,63,
52,53,54,55,   56,57,58,59,   60,61,255,255,  255,255,255,255,
255,0,1,2,    3,4,5,6,     7,8,9,10,     11,12,13,14,
15,16,17,18,   19,20,21,22,   23,24,25,255,   255,255,255,255,
255,26,27,28,   29,30,31,32,   33,34,35,36,   37,38,39,40,
41,42,43,44,   45,46,47,48,   49,50,51,255,   255,255,255,255
};
 
#define BVal(x) Base64IdxTab[x]
 
int CSeeBase64Dlg::DecodeBase64(char * pInput, char * pOutput)
{
    int i = 0;
    int iCnt = 0;
    int iSrcLen = (int)strlen(pInput);
 
    char * p = pOutput;
 
    for (i=0; i < iSrcLen; i++)
    {
        if (pInput[i] > 127) continue;
        if (pInput[i] == '=') return p-pOutput+1;
 
        BYTE a = BVal(pInput[i]);
        if (a == 255) continue;
        
        switch (iCnt)
        {
        case 0:
            {
                *p = a << 2;
                iCnt++;
            }
            break;
 
        case 1:
            {
                *p++ |= a >> 4;
                *p = a << 4;
                iCnt++;
            }
            break;
 
        case 2:
            {
                *p++ |= a >> 2;
                *p = a << 6;
                iCnt++;
            }
            break;
        case 3:
            {
                *p++ |= a;
                iCnt = 0;
            }
            break;
        }
    }
 
    *p = 0x00;
    return p-pOutput;
}

延伸 · 阅读

精彩推荐