BASE64编码可以看作是一种数据可视化的编码集,也可以看作是一种简单的数据加密处理。即展现出数据编码的思路,也满足了数据可视化的要求。同时展现出一种简单的数据加密方法。
在处理很多数据缓冲区的时候,对于不可见字符,都比较头疼,不知道这些不可见字符到底是什么,即使是比对他们的ASSCII,也比较麻烦,需要中断或者调试或者修改代码等到。所以很多时候会将这些不可见字符转换为可视化的串,从而检查调试其数值是否正确。可视化的过程当然可以选择BASE64,同样也可以选择其他的方式,如直接将每个字节转换成两位十六进制数,也同样可以进行比较,而且不会破坏字节与字节之间的关系,显得更加方便。
同样是三个字节,转换为BASE64编码需要4个字节,转换为16进制则需要6个字节,因此,相对于16进制表示法,BASE64可以减少数据长度。从编码学的角度来看,也可以很好的理解,对于每个字节,BASE64使用了6比特,而十六进制只使用四字节(编码16个字符只需要4字节,但是表示的话,还是每个字节表示一个字符)。
BASE64编码规则:将原字符串顺序切割为长度3的分组(最后一个分组长度不一定为3),然后将每个分组转换为4字节的BASE64编码,对于长度为3的分组,将3个字节转换为二进制,总共24比特,将这24比特顺次切割为4个6比特的分组,再把每个分组高位补两个0,补足8比特。根据下面的BASE64编码表将4个8比特字节转换为对应的字符;对于长度为1的分组,取高6位生成第一位BASE64编码,低两位右补4个0,并转换为第二个BASE64编码,然后在这两个编码后补两个等号“=”,生成四位BASE64编码;对于长度为2的分组,处理逻辑类似与长度为1的分组,将2字节的16位二进制从左到右顺次分割,第一个6比特生成第一个BASE64编码,第二个6比特生成第二个BASE64编码,剩余的四比特右边补两个0,生成第三个BASE64编码,后面补一个“=”号,凑足4个BASE64编码。然后按照顺序,将多组BASE64编码串起来,生成最终的BASE64编码串。
BASE64解码规则:将BASE64编码的串切割为宽4的分组,对于每一个分组,将四个字符转换成对应的BASE64编码值二进制,取每个字节的低6位,并将这六位按顺序连接起来,得到24比特,将这24比特分割为3个8比特字节。然后还原成三个ASCII字符。如果带BASE64编码带等号的话,跟编码规则相反,一个等号时,得到2个原字符,两个等号时,得到一个原字符。最后把所有解码得到的字符分组按顺序串联起来。
下面给出基于C的BASE64实现,使用了字典表,比较方便,高效:
// base_base64.h
#ifndef __BASE_BASE64_H__
#define __BASE_BASE64_H__
int encode_base64(const char *src,int src_len,char *dst,int *dst_len);
int decode_base64(const char *src,int src_len,char *dst,int *dst_len);
#endif
// base_base64.c
#include <stdio.h>
#include <string.h>
#include "base_define.h"
#include "base_base64.h"
char encode_base64_dict[] ={"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"};
char decode_base64_dict[] ={
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,62, 0, 0, 0,63,
52,53,54,55,56,57,58,59,60,61, 0, 0, 0, 0, 0, 0,
0, 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, 0, 0, 0, 0, 0,
0,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, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
int encode_base64(const char *src,int src_len,char *dst,int *dst_len){
int ret = BASE_RET_SUCCESS;
int dst_index=0;
int group_num,last_num,i,k;
group_num = src_len/3;
last_num = src_len %3;
i = (src_len + 2)/3;
if(i * 4 > (*dst_len)){
ret = BASE_RET_FAILED;
return ret;
}
memset(dst,0,*dst_len);
for(i=0;i<group_num;i++){
k = 3*i;
dst[dst_index++] = encode_base64_dict[(src[k]>>2) & 0X3F];
dst[dst_index++] = encode_base64_dict[((((src[k])&0X03)<<4)| (src[k+1])>>4) & 0X3F];
dst[dst_index++] = encode_base64_dict[(((src[k+1]&0X0F)<<2) | (src[k+2])>>6) & 0X3F];
dst[dst_index++] = encode_base64_dict[((src[k+2]) & 0X3F)];
}
if(last_num == 2){
k = group_num * 3;
dst[dst_index++] = encode_base64_dict[((src[k])>>2) & 0X3F];
dst[dst_index++] = encode_base64_dict[((((src[k])&0X03)<<4) | (src[k+1]>>4)) & 0X3F];
dst[dst_index++] = encode_base64_dict[(((src[k+1])&0X0F)<<2) & 0X3F];
dst[dst_index++] = '=';
}else if(last_num == 1){
k = group_num * 3;
dst[dst_index++] = encode_base64_dict[((src[k])>>2) & 0X3F];
dst[dst_index++] = encode_base64_dict[(((src[k])&0X03)<<4) & 0X30];
dst[dst_index++] = '=';
dst[dst_index++] = '=';
}
ret = BASE_RET_SUCCESS;
*dst_len = dst_index;
return ret;
}
int decode_base64(const char *src,int src_len,char *dst,int *dst_len){
int ret = BASE_RET_SUCCESS;
int group_num,last_num,dst_index = 0;
group_num = src_len / 4;
last_num = src_len %4;
if((last_num > 0) || (group_num * 3 > (*dst_len))){
ret = BASE_RET_FAILED;
return ret;
}
int i,k;
char tmp_val[4] = {0};
k = -4;
for(i = 0;i<group_num ;i++){
k = k + 4;
tmp_val[0] = decode_base64_dict[src[k]];
tmp_val[1] = decode_base64_dict[src[k+1]];
tmp_val[2] = decode_base64_dict[src[k+2]];
tmp_val[3] = decode_base64_dict[src[k+3]];
dst[dst_index++] = ((tmp_val[0]<<2) | (tmp_val[1]>>4));
dst[dst_index++] = ((tmp_val[1] & 0X0F) <<4) | ((tmp_val[2])>>2);
dst[dst_index++] = ((tmp_val[2] & 0X03) <<6) | tmp_val[3];
}
for(i = dst_index-1;i> 0;i--){
if(dst[i] == 0){
dst_index--;
}else{
break;
}
}
ret = BASE_RET_SUCCESS;
*dst_len = dst_index;
return ret;
}
使用字典表,可以直接通过字典查使用下标转换编码。