一、Base64是什么?
Base64是一种字节码编码方式,主要是基于64个可打印字符来表示二进制数据的方法,详细介绍请参考百度百科。[百度百科介绍]
二、示例源码(C语言接口)
源程序主要包含base64编码与base64解码接口。main函数主要作为demo展示用,代码里有对文件(比如jpg图片等)编解码操作示例。
#include <stdio.h>
#include <string.h>
#include <math.h>
/* Base64编码映射表 */
const char *const Base64Table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
/********************************************************
*功能描述:Base64编码
*输入参数: Bindata:原始bin数据
Binlen:原始 bin数据长度
*输出参数:Base64Buf:base64编码数据
*返 回 值:base64编码后数据长度
*********************************************************/
int Base64Encode(const unsigned char *Bindata, int Binlen, char *const Base64Buf)
{
unsigned char s8CharIndex = 0;
int i=0, Len=0;
for ((i=0,Len=0); i<Binlen; i+=3)
{
s8CharIndex = (Bindata[i]>>2);
s8CharIndex &= (unsigned char)0x3F;
Base64Buf[Len++] = Base64Table[(int)s8CharIndex];
s8CharIndex = ((unsigned char)(Bindata[i]<<4)) & ((unsigned char)0x30);
if ((i+1) >= Binlen)
{
Base64Buf[Len++] = Base64Table[(int)s8CharIndex];
Base64Buf[Len++] = '=';
Base64Buf[Len++] = '=';
break;
}
s8CharIndex |= ((unsigned char)(Bindata[i+1]>>4)) & ((unsigned char)0x0F);
Base64Buf[Len++] = Base64Table[(int)s8CharIndex];
s8CharIndex = ((unsigned char)(Bindata[i+1]<<2)) & ((unsigned char)0x3C);
if ((i+2) >= Binlen)
{
Base64Buf[Len++] = Base64Table[(int)s8CharIndex];
Base64Buf[Len++] = '=';
break;
}
s8CharIndex |= ((unsigned char)(Bindata[i+2]>>6) & ((unsigned char)0x03));
Base64Buf[Len++] = Base64Table[(int)s8CharIndex];
s8CharIndex = ((unsigned char)Bindata[i+2]) & ((unsigned char)0x3F) ;
Base64Buf[Len++] = Base64Table[(int)s8CharIndex];
}
return Len;
}
/********************************************************
*功能描述:Base64解码
*输入参数: Base64Buf:base64编码数据
*输出参数:Bindata:解码后bin数据
*返 回 值:解码后bin数据长度
*********************************************************/
int Base64Decode(const char *Base64Buf, unsigned char *const Bindata)
{
int i, Len=0;
unsigned char s8CharIndex = 0;
unsigned char temp[4] = {0};
for ((i=0,Len=0); Base64Buf[i]!='\0'; i+=4)
{
memset(temp, 0xFF, sizeof(temp));
for (s8CharIndex=0; s8CharIndex<64; s8CharIndex++)
{
if (Base64Table[s8CharIndex] == Base64Buf[i])
temp[0]= s8CharIndex;
}
for (s8CharIndex=0; s8CharIndex<64; s8CharIndex++)
{
if (Base64Table[s8CharIndex] == Base64Buf[i+1])
temp[1]= s8CharIndex;
}
for (s8CharIndex=0; s8CharIndex<64; s8CharIndex++)
{
if (Base64Table[s8CharIndex] == Base64Buf[i+2])
temp[2]= s8CharIndex;
}
for (s8CharIndex=0; s8CharIndex<64; s8CharIndex++)
{
if (Base64Table[s8CharIndex] == Base64Buf[i+3])
temp[3]= s8CharIndex;
}
if ((0xFF==temp[0]) || (0xFF==temp[1]) || (0xFF==temp[2]) || (0xFF==temp[3]))
{
//printf("(%s:%d) already decode base64 Len:%d\r\n", __func__, __LINE__, i);
//break;//考虑到有些base64是经过变异的,不做退出处理
}
Bindata[Len++] = ((unsigned char)(((unsigned char)(temp[0] << 2))&0xFC)) |
((unsigned char)((unsigned char)(temp[1]>>4)&0x03));
if (Base64Buf[i+2] == '=')
{
printf("(%s:%d) already decode base64 Len:%d\r\n", __func__, __LINE__, i);
break;
}
Bindata[Len++] = ((unsigned char)(((unsigned char)(temp[1] << 4))&0xF0)) |
((unsigned char)((unsigned char)(temp[2]>>2)&0x0F));
if (Base64Buf[i+3] == '=')
{
printf("(%s:%d) already decode base64 Len:%d\r\n", __func__, __LINE__, i);
break;
}
Bindata[Len++] = ((unsigned char)(((unsigned char)(temp[2] << 6))&0xF0)) |
((unsigned char)(temp[3]&0x3F));
}
return Len;
}
int main(int argc, char **argv)
{
int s32Base64Len = 0;
int s32BinLen = 0;
int s32ReadLen = 0;
#if 0 //文件解码
FILE *fpEnc = NULL;
FILE *fpDec = NULL;
fpEnc = fopen(argv[1], "rb");//打开base64编码后的文件
if (NULL == fpEnc)
{
printf("fopen %s failed.\r\n", argv[1]);
return 0;
}
clearerr(fpEnc);
fseek(fpEnc, 0, SEEK_END);
if (0 >= (s32Base64Len=ftell(fpEnc)))
{
printf("ftell %d failed.\r\n", s32Base64Len);
return 0;
}
fseek(fpEnc, 0, SEEK_SET);
fpDec = fopen(argv[2], "wb");//打开待保存bin数据
if (NULL == fpDec)
{
printf("fopen %s failed.\r\n", argv[2]);
return 0;
}
clearerr(fpDec);
char Base64buf[s32Base64Len+1];
unsigned char Binbuf[((int)ceil((double)s32Base64Len/(double)4))*3];
/*TODO:实际可能一次读取不完或读取出错,所以如果优化要做循环读取一次解码一次的话,
需每次读取base64数据长度应为4的整数倍,否则解码会异常 */
memset(Base64buf, 0x00, sizeof(Base64buf));
s32ReadLen = fread(Base64buf, 1, s32Base64Len, fpEnc);
printf("fread Base64 Len=%d.\r\n", s32ReadLen);
memset(Binbuf, 0x00, sizeof(Binbuf));
s32BinLen = Base64Decode((const char *)Base64buf, Binbuf);
printf("Base64Decode BinLen=%d.\r\n", s32BinLen);
fwrite(Binbuf, 1, s32BinLen, fpDec);
if (NULL != fpEnc)
{
fclose(fpEnc);
fpEnc = NULL;
}
if (NULL != fpDec)
{
fflush(fpDec);
fclose(fpDec);
fpDec = NULL;
}
#elif 0//文件编码
FILE *fpEnc = NULL;
FILE *fpDec = NULL;
fpEnc = fopen(argv[2], "wb");//打开base64编码后的文件
if (NULL == fpEnc)
{
printf("fopen %s failed.\r\n", argv[1]);
return 0;
}
clearerr(fpEnc);
fpDec = fopen(argv[1], "rb");//打开未编码的源文件
if (NULL == fpDec)
{
printf("fopen %s failed.\r\n", argv[2]);
return 0;
}
clearerr(fpDec);
fseek(fpDec, 0, SEEK_END);
if (0 >= (s32BinLen=ftell(fpDec)))
{
printf("ftell %d failed.\r\n", s32BinLen);
return 0;
}
fseek(fpDec, 0, SEEK_SET);
char Base64buf[((int)ceil((double)s32BinLen/(double)3))*4+1];
unsigned char Binbuf[s32BinLen];
memset(Base64buf, 0x00, sizeof(Base64buf));
memset(Binbuf, 0x00, sizeof(Binbuf));
/*TODO:实际可能一次读取不完或读取出错,所以如果优化要做循环读取一次解码一次的话,
需每次读取base64数据长度应为3的倍数 */
s32ReadLen = fread(Binbuf, 1, s32BinLen, fpDec);
printf("fread Bin Len=%d.\r\n", s32ReadLen);
s32Base64Len = Base64Encode(Binbuf, s32BinLen, Base64buf);
printf("Base64Encode Base64Len=%d.\r\n", s32Base64Len);
fwrite(Base64buf, 1, s32Base64Len, fpEnc);
if (NULL != fpDec)
{
fclose(fpDec);
fpDec = NULL;
}
if (NULL != fpEnc)
{
fflush(fpEnc);
fclose(fpEnc);
fpEnc = NULL;
}
#elif 1//string编码
char *pstr = "hello world";
char Base64buf[((int)ceil((double)strlen(pstr)/(double)3))*4+1];
memset(Base64buf, 0x00, sizeof(Base64buf));
s32Base64Len = Base64Encode((const unsigned char*)pstr, strlen(pstr), Base64buf);
printf("base64:%s,Len:%d\r\n", Base64buf, s32Base64Len);
#endif
}
三、可打印字符表
正是由于Base64是基于64个可打印字符来表示二进制数据的,而对应可打印字符表的索引区间为0-63。所以在编码过程中最关键的是要求把每三个8Bit的字节转换为四个6Bit的字节(3x8 = 4x6 = 24Bit),然后把6Bit高位再添两位0,组成四个8Bit的字节(每个字节所能表示最大区间为0000 0000 <=>0011 1111对应十进制0-63),再将这四个8Bit字节作为可打印字符表索引号,取出索引号对应字符就是我们要的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 |
------注:文中难免存在文字与内容有遗漏和不妥之处,恳请各位在评论区批评指正,感谢!