编解码:Base64编解码
编解码:十六进制编解码中提到,编解码本质上是以不同的数据形式来展示“信息”,可以用二进制来表示,可以用十六进制来表示。
二进制的数据,在计算机中通常是不可读、不可打印的。
那有没有一种方式,可以让二进制的数据变成“可读可打印”的?
方法有很多,最常见的是十六进制编解码和Base64编解码。
十六进制编解码之前已介绍过,本文介绍Base64编解码。
百度百科:Base64是一种基于64个可打印字符来表示二进制数据的方法。
Base64编码要求把3个8位字节(3*8=24)转化为4个6位的字节(4*6=24),之后在6位的前面补两个0,形成8位一个字节的形式。 如果剩下的字符不足3个字节,则用0填充,输出字符使用’=’,因此编码后输出的文本末尾可能会出现1或2个’=’(只可能出现0/1/2个等号,否则就是错误的)。
为啥只可能出现0/1/2个等号?动手算算就知道。
将所有的bit位用8n表示(n表示待编码数据字节数量),则当:
n=1时,8*1 mod 6 = 2bit,需要补2个等号
n=2时,8*2 mod 6 = 4bit,需要补1个等号
n=3时,8*3 mod 6 = 0bit,需要补0个等号
n=4时,8*4 mod 6 = 2bit,需要补2个等号
……
Base64编解码前后空间大小变化:
6字节的数据,在编码后占用8字节空间;空间多占用了2B,比原来大:2B/6B=1/3
8字节的数据,在解码后占用6字节空间;空间少占用了2B,比原来小:2B/8B=1/4
Base64编码使得待编码数据增大,增加到原大小的4/3;
Base64解码使得戴洁马数据减小,减小到原大小的3/4;
Demo:
待转换数据(3字节24bit):0x61(a), 0x62(b), 0x63(c):
二进制形式:01100001 01100010 01100011
分组(每6bit一组*4组):011000 010110 001001 100011
每组高位补两个bit的0:00011000 00010110 00001001 00100011
十进制:24 22 9 35
参照对照表(摘自:http://base64.xpcha.com/):

Base64结果:YWJj
待转换数据(1字节8bit):0x61(a)
二进制形式:01100001
不是6bit整数,需要多补4个bit 0,结果为:01100001 0000
分组:011000 010000
高位补两个bit的0:00011000 00010000
十进制:24 16
转换结果:Y Q
由于Base64规定,Base64结果长度一定是4的倍数,如果不够则以’=’补全,则实际Base64结果为:
YQ==
Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static const unsigned char *base64=(unsigned char *)"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
/*-----------------------------------------------------------------------------
* 函数名称:Base64Encode
* 功能描述:Base64编码
* 参数说明:base64code:编码结果缓冲区;src:待编码数据;src_len待编码数据长度
* 返 回 值:编码结果长度(字节)
* 备 注:注意留有足够的结果缓冲区(src_len/3*4)
* */
static int Base64Encode(char *base64code, const char *src, int src_len)
{
int n,buflen,i,j;
if (src_len == )
src_len = strlen(src);
buflen=n=src_len;
for (i=,j=; i<=buflen-; i+=,j+=)
{
base64code[j] = (src[i]&)>>;
base64code[j+] = ((src[i]&)<<) + ((src[i+]&)>>);
base64code[j+] = ((src[i+]&)<<) + ((src[i+]&)>>);
base64code[j+] = src[i+]&;
}
if (n%==)
{
base64code[j] = (src[i]&)>>;
base64code[j+] = ((src[i]&)<<);
base64code[j+] = ;
base64code[j+] = ;
j += ;
}
else if (n%==)
{
base64code[j] = (src[i]&)>>;
base64code[j+] = ((src[i]&)<<) + ((src[i+]&)>>);
base64code[j+] = ((src[i+]&)<<);
base64code[j+] = ;
j += ;
}
for (i=; i<j; i++)
base64code[i] = base64[(int)base64code[i]];
base64code[j] = ;
return j;
}
/*-------------------------------------------------------------------------------
* 函数名称:Base64Decode
* 功能描述:Base64解码
* 参数说明:buf:解码结果缓冲区;base64code:待解码数据;src_len:待解码数据长度
* 返 回 值:解码结果长度(字节)
* 备 注:注意,buf中解码结果缓冲区中数据不一定可打印
* */
static int Base64Decode(char *buf, const char *base64code, int src_len)
{
int n,i,j,pad;
if (src_len == )
src_len = strlen(base64code);
if (src_len% != )
return -;
unsigned char *p=;
unsigned char *src=;
pad = ;
n = src_len;
src = (unsigned char *)malloc(n);
for (i=; i<n; i++)
src[i]=base64code[i];
while (n>&&src[n-]=='=')
{
src[n-] = ;
pad++;
n--;
}
for(i=; i<n; i++)
{
p = (unsigned char *)strchr((const char *)base64, (int)src[i]);
if (!p)
{
free(src);
return -;
}
src[i] = p-(unsigned char *)base64;
}
for (i=,j=; i<n; i+=,j+=)
{
buf[j] = (src[i]<<) + ((src[i+]&)>>);
buf[j+] = ((src[i+]&)<<) + ((src[i+]&)>>);
buf[j+] = ((src[i+]&)<<) + src[i+];
}
j -= pad;
buf[j] = ;
free(src);
return j;
}
int main()
{
// 测试Base64编码
{
char buff[] = "";
char *str = "0123456789abcdef";
memset(buff, , ); // clear
int base64len = Base64Encode(buff, str, strlen(str));
buff[base64len] = ;
printf("%s\n", buff);
}
// 测试Base64解码
{
char buff[] = "";
char *str = "MDEyMzQ1Njc4OWFiY2RlZg==";
memset(buff, , );
int base64len = Base64Decode(buff, str, strlen(str));
printf("%s\n", buff);
}
return ;
}
编译 && 执行:
[jiang@localhost jiang]$ gcc -o Base64 Base64.c
[jiang@localhost jiang]$ ./Base64
MDEyMzQ1Njc4OWFiY2RlZg==
89abcdef
需要注意:
为啥在编码时需要指明待编码数据长度?
原因是待编码数据不一定可读,是二进制数据,即,可能存在0x00,很显然不可以用strlen,那我在编码函数中如何知道其待编码数据长度呢?不知道!所以,调用编码函数时需要显示地指明待编码数据长度。
引用:
1.http://base64.xpcha.com/