前两天在做一个项目时,合作方的开发人员说需要用到Base64 编码。由于之前没听说过这种编码,马上上网google 了下资料,才发现Base64 编码使用得这么普遍,最常用的就是电子邮件传输编码方式。进一步查找资料发现,迅雷的下载地址链接也使用这种编码方式。
引用维基百科中对Base64 编码的介绍如下:
“在MIME 格式的电子邮件中,base64 可以用来将binary 的字节序列数据编码成ASCII 字符序列构成的文本。使用时,在传输编码方式中指定base64 。使用的字符包括大小写字母各26 个,加上 10 个数字,和加号「+ 」,斜杠「 / 」,一共 64 个字符,等号「= 」用来作为后缀用途。
完整的base64 定义可见 RFC 1421和 RFC 2045。编码后的数据比原始数据略长,为原来的4/3 。在电子邮件中,根据RFC 822规定,每76 个字符,还需要加上一个回车换行。可以估算编码后数据长度大约为原长的135.1% 。
转换的时候,将三个byte 的数据,先后放入一个24bit 的缓冲区中,先来的byte 占高位。数据不足3byte 的话,于缓冲区中剩下的bit 用 0 补足。然后,每次取出6 (因为 26 = 64 )个bit ,按照其值选择ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ 中的字符作为编码后的输出。不断进行,直到全部输入数据转换完成。如果最后剩下两个输入数据,在编码结果后加 1 个「 = 」;如果最后剩下一个输入数据,编码结果后加2 个「 = 」;如果没有剩下任何数据,就什么都不要加,这样才可以保证数据还
原的正确性。”
知道了编码的原理后,又上网搜索了有没开源的lib 可用,发现网站libb64: Base64 Encoding/Decoding Routines下已有完整的编码/ 解码源代码可用,下载下来对某一通过Base64 编码的字串进行解码,顺利通过了。试了下编码的功能,发现编码得到的字串在末尾总与标准有差异,查看了源代码,发现其编码并没有按照标准进行。
于是,马上冒出了自己重写Base64 编码的C 语言代码的念头(后来索性把解码也重新实现了一遍),就当是巩固移位运算的操作吧。虽然编码/ 解码的原理比较简单,但实现完了还是比较开心的,下面把自己实现的编码/ 解码的代码贴出来:
// file: base64_encode.c
const int CHARS_PER_LINE = 76;
int base64_encode(unsigned char *src, long len, char *dest)
{
const char *encode_table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
char *ori_dest = dest;
int i = 0, step_count = 0;
int n, r;
while (i + 3 <= len)
{
n = *(src + i) << 16;
n |= *(src + i + 1) << 8;
n |= *(src + i + 2);
*(dest++) = encode_table[(n & 0x00FC0000) >> 18];
*(dest++) = encode_table[(n & 0x0003F000) >> 12];
*(dest++) = encode_table[(n & 0x00000FC0) >> 6];
*(dest++) = encode_table[(n & 0x0000003F)];
if (++step_count == CHARS_PER_LINE / 4)
{
step_count = 0;
*(dest++) = '/n';
}
i += 3;
}
if (r = len % 3)
{
if (r == 1)
{
n = *(src + i) << 16;
*(dest++) = encode_table[(n & 0x00FC0000) >> 18];
*(dest++) = encode_table[(n & 0x0003F000) >> 12];
*(dest++) = '=';
*(dest++) = '=';
}
if (r == 2)
{
n = *(src + i) << 16;
n |= *(src + i + 1) << 8;
*(dest++) = encode_table[(n & 0x00FC0000) >> 18];
*(dest++) = encode_table[(n & 0x0003F000) >> 12];
*(dest++) = encode_table[(n & 0x00000FC0) >> 6];
*(dest++) = '=';
}
if (++step_count == CHARS_PER_LINE / 4)
{
*(dest++) = '/n';
}
}
*dest = '/0';
return dest - ori_dest;
}
// file: base64_decode.c
#include <string.h>
#include <ctype.h>
static long check_length(char *src)
{
char *p = strchr(src, '=');
if (p == NULL)
return strlen(src);
else
return p - src;
}
static char base64_decode_digit(char ch)
{
switch (ch)
{
case '+':
return 62;
case '/':
return 63;
default:
if (isdigit(ch))
return ch - '0' + 52;
else if (islower(ch))
return ch - 'a' + 26;
else if (isupper(ch))
return ch - 'A';
}
return 0xFF;
}
int base64_decode(char *src, unsigned char *dest)
{
int i = 0, n, r;
long len;
unsigned char *ori_dest = dest;
len = check_length(src);
while (i + 4 <= len)
{
n = base64_decode_digit(*(src + i)) << 18;
n |= base64_decode_digit(*(src + i + 1)) << 12;
n |= base64_decode_digit(*(src + i + 2)) << 6;
n |= base64_decode_digit(*(src + i + 3));
*(dest++) = n >> 16;
*(dest++) = (n >> 8) & 0xFF;
*(dest++) = n & 0xFF;
if (*(src + i + 4) == '/n')
i++;
i += 4;
}
if (r = len % 4)
{
if (r == 2)
{
n = base64_decode_digit(*(src + i)) << 18;
n |= base64_decode_digit(*(src + i + 1)) << 12;
*(dest++) = n >> 16;
}
if (r == 3)
{
n = base64_decode_digit(*(src + i)) << 18;
n |= base64_decode_digit(*(src + i + 1)) << 12;
n |= base64_decode_digit(*(src + i + 2)) << 6;
*(dest++) = n >> 16;
*(dest++) = (n >> 8) & 0xFF;
}
}
*dest = '/0';
return dest - ori_dest;
}
用C语言实现编码/解码的功能后,对任意一字串进行编码后与网站(站长工具)https://tool.chinaz.com/Tools/Base64.aspx(一个在线测试Base64编码的网站)下编码的结果进行比较,发现是一样的。又对编码后的字串进行解码,也顺利还原了:)