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

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;
}

延伸 · 阅读

精彩推荐
  • C/C++c/c++内存分配大小实例讲解

    c/c++内存分配大小实例讲解

    在本篇文章里小编给大家整理了一篇关于c/c++内存分配大小实例讲解内容,有需要的朋友们可以跟着学习参考下。...

    jihite5172022-02-22
  • C/C++C语言实现双人五子棋游戏

    C语言实现双人五子棋游戏

    这篇文章主要为大家详细介绍了C语言实现双人五子棋游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    两片空白7312021-11-12
  • C/C++c/c++实现获取域名的IP地址

    c/c++实现获取域名的IP地址

    本文给大家汇总介绍了使用c/c++实现获取域名的IP地址的几种方法以及这些方法的核心函数gethostbyname的详细用法,非常的实用,有需要的小伙伴可以参考下...

    C++教程网10262021-03-16
  • C/C++深入C++拷贝构造函数的总结详解

    深入C++拷贝构造函数的总结详解

    本篇文章是对C++中拷贝构造函数进行了总结与介绍。需要的朋友参考下...

    C++教程网5182020-11-30
  • C/C++关于C语言中E-R图的详解

    关于C语言中E-R图的详解

    今天小编就为大家分享一篇关于关于C语言中E-R图的详解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看...

    Struggler095962021-07-12
  • C/C++使用C++制作简单的web服务器(续)

    使用C++制作简单的web服务器(续)

    本文承接上文《使用C++制作简单的web服务器》,把web服务器做的功能稍微强大些,主要增加的功能是从文件中读取网页并返回给客户端,而不是把网页代码...

    C++教程网5492021-02-22
  • C/C++OpenCV实现拼接图像的简单方法

    OpenCV实现拼接图像的简单方法

    这篇文章主要为大家详细介绍了OpenCV实现拼接图像的简单方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    iteye_183805102021-07-29
  • C/C++C语言main函数的三种形式实例详解

    C语言main函数的三种形式实例详解

    这篇文章主要介绍了 C语言main函数的三种形式实例详解的相关资料,需要的朋友可以参考下...

    ieearth6912021-05-16