Base64在线转换工具


一、为什么做这个项目?(项目背景)

在计算机中一个字节共有256种,即ascii码表,而ascii码的128~255之间的值是不可见字符,对于一些只支持可见字符的协议,比如邮件传输协议(SMTP)只支持可见的ASCII字符的传递,如果要传输二进制文件,比如:图片、视频是无法实现的,因此就有了Base64编码格式,Base64编码格式对于所有二进制格式的数据,都可以转化为可显示的字符。

  1. 在网络上交换数据时,比如说从A地传递到B地,往往要经过多个路由设备,由于不同的设备对字符的处理方式有一些不同,这样那些不可见字符就有可能被处理错误,不利于传输。
  2. http先以当中的key-value字段,必须进行url编码,不然出现的等号或者空格可能是解析失败
  3. 有些文本协议不支持可见字符传输,比如简单邮件传输协议(SMTP)
  4. 网页中内嵌简单图片

二、这个项目能实现怎么样的功能?(项目目标)

实现一个在线的base64转换工具,支持文本的base64可逆转化以及图片 base64转换。

三、项目用什么框架来实现的?(项目框架)

在这里插入图片描述

四、具体怎么实现的?(具体实现)

1.base64算法+实现
2.简单的前端知识:html + CSS + javascript + ajax
3.页面搭建
4.搭建http服务器 ---->使用httplib库:只需要包含头文件
5.前后端调通

1.编码/解码算法模块

base64编码之所以叫base64,是因为其使用64个字符来对二进制数据进行编码

A~Z,a~z,0~9,+ / 一共64个
从0到63:
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
 = 用来不够补位

1.1编码过程

对于用户给的二进制比特流,三个三个字节一组进行编码
3个字节 -------->base64 --------->4个可以显示的字节
24个比特位---->base64 ---------->将24个比特位均分为4组,每组6个比特位------>转为为4个字节
在这里插入图片描述

举个例子
字符串长度被3整除时
使用base64编码:man
在这里插入图片描述

字符串长度不能被3整除时
使用base64编码:manm
在这里插入图片描述

当需要编码的字节数不能被3整除时,最后会多出一个或者两个字节,先用0字节值在末尾补足,使其能够被3整除,然后进行base64编码,最后在编码结果后补上一个或者两个=号,代表补足的字节数。
比如传4个字节,这时多出一个字节,就要补两个字节的0(16个位的0),然后再编码,最后编码结果后补两个 = 符号。

扫描二维码关注公众号,回复: 12830672 查看本文章

注意:base64一行最多显示76个可显字符
缺陷:base64将三个字节转化成四个字节,因此Base64编码后的文本,会比原文本大出三分之一左右

编码详细思路

1.对字符按照三个三个进行分组:ch1、ch2、ch2
2.关键操作
能被3整除的字节数:
ch1:直接取其高6位-----> ch1>>2 ------>比如M(01001101) 右移两位------>00010011 ------>转换十进制19 ----->查base64表:T
将ch1中末尾还剩余的两位,与ch2中高4四位进行拼接,拼接成6个比特位-------> ( ch1 << 4 | ch2 >> 4) & 0x3F,和0x3F按位与后相当于清零前两位,即在前两位补0
M(01001101) 左移4位后--------->11010000
a(01100001)右移4位后----------->00000110
| 拼接后 11010110
& 0x3F后 00010110 --------转换十进制22 ----->查base64表:W
ch2:还剩低4位,与ch3的高2位来进行拼接,拼接成6个比特位:(ch2 << 2 | ch3 >> 6) &0x3F
a(01100001)左移2位后----------->10000100
n(01101110)右移6位后-----------> 00000001
拼接后10000101
& 0x3F后 00000101--------转换十进制5 ----->查base64表:F
ch3:还剩低6位,成为一组,前面补两个0:(ch3 & 0x3F)
n(01101110) & 0x3F-----------> 00101110--------转换十进制46 ----->查base64表:u
最终得到base64编码:TWFu

不能被3整除的字节数:
补需要字节数的0,比如4个字节缺两个字节被3整除,则补2个字节的0。有多出一个字节的情况,也有多出两个字节的情况,基本操作和上述思路一致

编码模块代码

//编码模块
std::string Base64::Encode(const std::string& strData)//编码模块
{
	std::string strEncode;
	std::string strEncodeTable("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"); //Base64编码表
	unsigned char temp[4];
	size_t index = 0;
	size_t lineLength = 0;//记录base64每行的字节数
	for (size_t i = 0; i < strData.size()/3; ++i)
	{
		//3个字符为一组进行base64编码转换
		temp[1] = strData[index++];
		temp[2] = strData[index++];
		temp[3] = strData[index++];

		//转换后每个字节都由 补位的00 + 拼接后的6位 组成
		//取第一个字节的高6位
		strEncode += strEncodeTable[temp[1] >> 2];

		//取第一个字节的低2位 + 第二字节的高四位拼接
		strEncode += strEncodeTable[((temp[1] << 4) | (temp[2] >> 4)) & 0x3F];

		//取第二个字节的低4位 + 第三个字节的高2位拼接
		strEncode += strEncodeTable[((temp[2] << 2) | (temp[3] >> 6)) & 0x3F];

		//取第三个字节的低6位
		strEncode += strEncodeTable[temp[3] & 0x3F];

		lineLength += 4;
		if (lineLength == 76)//base64一行只能显示76的字节
		{
			strEncode += "\r\n";
			lineLength = 0;
		}
	}

	size_t mod = strData.size() % 3;//查看传入数据字节数多出3的倍数几个字节
	if (mod == 1)//如果多出3的倍数一个字节,补两个 = 符号
	{
		temp[1] = strData[index++];
		//取多出来的这个字节的高6位
		strEncode += strEncodeTable[temp[1] >> 2];
		//取多出来的这个字节的低2位 + 补4位的0进行拼接
		strEncode += strEncodeTable[(temp[1] & 0x03) << 4];
		//最后补两个 = 符号
		strEncode += "==";
	}
	else if (mod == 2)//如果多出3的倍数两个字节,补一个 = 符号
	{
		temp[1] = strData[index++];
		temp[2] = strData[index++];
		//取多出的第一个字节的高6位
		strEncode += strEncodeTable[temp[1] >> 2];
		//取多出的第一个字节的低2位 + 多出的第二个字节的高4位拼接
		strEncode += strEncodeTable[((temp[1] << 4) | (temp[2] >> 4)) & 0x3F];
		//取多出的第二个字节的低4位 + 补2位的0进行拼接
		strEncode += strEncodeTable[(temp[2] & 0x0F) << 2];
		//最后补一个 = 符号
		strEncode += "=";
	}
	return strEncode;
}

1.2解码过程

1.获取base64编码后的每个字符ch
2.获取ch在编码表中的下标
3.该下标就是原字符对应的6个比特位
解析出4组之后,就形成了24个比特位,然后将24个比特位划分为3个字节
解码过程就是把编码逆过来,以aGVsbG8=为例子,四个字节为一组进行解码,前四个解码过程如图,最终全部解码为hello
在这里插入图片描述

//快速解码表
const char DecodeTable[] =
{
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'-'9'
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, // 'A'-'Z'
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, // 'a'-'z'
};

解码详细思路

比如将 aGVsbG8= 进行解码
int nvalue = 0 nvalue: 00000000 00000000 00000000 00000000
四个一组进行解码aGVs:
DecodeTable[‘a’] ---->DecodeTable[97] = 26
-------->26(011010)存储到一个32位int型的第18~23比特的位置:(26 << 18) | nvalue-------> 00000000 01101000 00000000 00000000
DecodeTable[‘G’] ---->DecodeTable[71] = 6 -------->6(000110)
-------->6(000110)存储到一个32位int型的第12~17比特的位置:(6 << 12) | nvalue-------> 00000000 01101000 01100000 00000000
DecodeTable[‘V’] ---->DecodeTable[86] = 21 -------->21(010101)
-------->21(010101)存储到一个32位int型的第06~11比特的位置:(21 << 6) | nvalue-------> 00000000 01101000 01100101 01000000
DecodeTable[‘s’] ---->DecodeTable[115] = 44 -------->44(101100)
-------->44(101100)存储到一个32位int型的第00~05比特的位置:(44) | nvalue-------> 00000000 01101000 01100101 01101100
然后将nvalue从第23比特位开始八位八位读出共取三个字节就得到了解码后的数据:hel
00000000 01101000 01100101 01101100 ------->h(01101000)e(01100101)l(01101100)
bG8=同上解码过程,不同的是在解码时遇到 ‘=’ 就结束,解码结果:lo
aGVsbG8= 解码结果为:hello

编码模块代码

//解码模块
std::string Base64::Decode(const std::string& strData)//解码模块
{
	std::string strDecode;
	//快速解码表
	const char DecodeTable[] =
	{
	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'-'9'
	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, // 'A'-'Z'
	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, // 'a'-'z'
	};
	size_t value = 0; //保存解码的4组6个比特位(总共24个比特位)
	size_t index = 0;
	while (index < strData.size())
	{
		//编码时一行只能放置76个字符,超过76个字符会放到下一行
		if (strData[index] != '\r' && strData[index + 1] != '\n')//说明一行还没解码完毕
		{
			//解析第一个编码
			value = DecodeTable[strData[index++]] << 18;
			//解析第二个编码
			value = (DecodeTable[strData[index++]] << 12) | value;
			strDecode += ((value >> 16) & 0xFF);//存储第一个解码后的数据16-23位

			if (strData[index] != '=')
			{
				//解析第三个编码
				value = (DecodeTable[strData[index++]] << 6) | value;
				strDecode += ((value >> 8) & 0xFF);//存储第二个解码后的数据08-15位

				if (strData[index] != '=')
				{
					//解析第四个编码
					value = (DecodeTable[strData[index++]]) | value;
					strDecode += (value & 0xFF);//存储第三个解码后的数据00-07位
				}
			}
			else
			{
				break;
			}
		}
		else
		{
			//解码到该行的末尾了
			index += 2; //跳过 \r\n
		}
	}
	return strDecode;
}

(未完待续)

猜你喜欢

转载自blog.csdn.net/NanlinW/article/details/113480369