原理参照:BASE64编码简介
一、概念及原理:
BASE64是一种编码方式,通常用于把二进制数据编码为可写的字符形式的数据。这是一种可逆的编码方式。编码后的数据是一个字符串,其中包含的字符为:A-Z、a-z、0-9、+、/ 共64个字符(26 + 26 + 10 + 1 + 1 = 64)【注:其实是65个字符,“=”是填充字符】。
64个字符需要6位来表示,表示成数值为0~63。
这样,长度为3个字节的数据经过Base64编码后就变为4个字节。
例1: 字符串“Xue”经过Base64编码后变为“WHVl”。
长度为3个字节的数据位数是83=24,可以精确地分成64。
如果数据的字节数不是3的倍数,则其位数就不是6的倍数,那么需要就不能精确地划分成6位的块。
此时,需在原数据后面添加1个或2个零值字节,使其字节数是3的倍数。
然后,在编码后的字符串后面添加1个或2个等号“=”,表示所添加的零值字节数。
例2:字符串“Xu”经过Base64编码后变为“WHU=”。
例3:字符串“X”经过Base64编码后变为“WA==”。
二、c#实现base64编解码算法
基于以上原理,实现的base64编解码算法如下:
/// <summary>
/// Base64编解码
/// </summary>
public static class Base64
{
private static readonly char[] metachars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".ToCharArray();
private static readonly char fixchar = '=';
public static string Encode(byte[] arr)
{
var srcList = arr.ToList();
StringBuilder sb = new StringBuilder();
int yushu = srcList.Count % 3;
for (int i = 0; yushu > 0 && i < 3 - yushu; i++)
{
srcList.Add(0);
}
for (int i = 0; i <= srcList.Count - 3; i += 3)
{
//是否是最后一个变换
bool isLast = i == srcList.Count - 3;
//先取出三个字节
byte b1 = srcList[i];
byte b2 = srcList[i + 1];
byte b3 = srcList[i + 2];
//开始组装四个目标字节
//第一个目标字节直接右移两位即可
byte d1 = (byte)((b1 >> 2) & (0B0011_1111));
sb.Append(metachars[d1]);
//第二个目标字节取第一位的后两位和第二字节的前四位
byte b1_2 = (byte)((b1 << 4) & 0B0011_0000);
byte b2_4_1 = (byte)((b2 >> 4) & 0B0000_1111);
byte d2 = (byte)(b1_2 | b2_4_1);
sb.Append(metachars[d2]);
//第三个目标字节取第二字节的后四位和第三字节的前两位
if (isLast && yushu == 1)
{
//最后一圈余数为1,补了两个字节,故第三个字节是fixchar
sb.Append(fixchar);
}
else
{
byte b2_4_2 = (byte)((b2 << 2) & 0B0011_1100);
byte b3_2 = (byte)((b3 >> 6) & 0B0000_0011);
byte d3 = (byte)(b2_4_2 | b3_2);
sb.Append(metachars[d3]);
}
//第四个目标字节取第三个字节的后六位
if (isLast && yushu > 0)
{
sb.Append(fixchar);
}
else
{
byte d4 = (byte)(b3 & 0B0011_1111);
sb.Append(metachars[d4]);
}
}
return sb.ToString();
}
public static byte[] Decode(string str)
{
//首先补充fixchar
var yushu = str.Length % 4;
for (int i = 0; yushu > 0 && i < 4 - yushu; i++)
{
yushu += fixchar;
}
int count = str.Count(c => c == fixchar);
List<byte> dest = new List<byte>();
var metalist = metachars.ToList();
char[] arr = str.ToCharArray();
for (int i = 0; i <= arr.Length - 4; i += 4)
{
char c1 = arr[i];
char c2 = arr[i + 1];
char c3 = arr[i + 2];
char c4 = arr[i + 3];
byte b1 = (byte)metalist.IndexOf(c1);
byte b2 = (byte)metalist.IndexOf(c2);
byte b3 = (byte)metalist.IndexOf(c3);
byte b4 = (byte)metalist.IndexOf(c4);
//开始组装三个目标字节
//第一个目标字节是第一个字节的345678位和第二个字节的34位
byte b1_345678 = (byte)(b1 << 2 & 0B1111_1100);
byte b2_34 = (byte)(b2 >> 4 & 0B0000_0011);
dest.Add((byte)(b1_345678 | b2_34));
//第二个目标字节是第二个字节的5678位和第三个字节的3456位
if (c3 == fixchar)
{
//第二个字节是补充的且到了末尾
break;
}
else
{
byte b2_5678 = (byte)(b2 << 4 & 0B1111_0000);
byte b3_3456 = (byte)(b3 >> 2 & 0B0000_1111);
dest.Add((byte)(b2_5678 | b3_3456));
}
//第三个目标字节是第三个字节的78位和第四个字节的345678位
if (c4 == fixchar)
{
//第四个字节是补充的且到了末尾
break;
}
else
{
byte b3_78 = (byte)(b3 << 6 & 0B1100_0000);
byte b4_345678 = (byte)(b4 & 0B0011_1111);
dest.Add((byte)(b3_78 | b4_345678));
}
}
return dest.ToArray();
}
}
测试程序如下:
private static void TestBase64Encode()
{
//Xue=>WHVl
byte[] arr = Encoding.ASCII.GetBytes("Xue");
var dest = Base64.Encode(arr);
Console.WriteLine($"Xue.base64=>{dest}");
//WHVl=>Xue
arr = Base64.Decode("WHVl");
dest = Encoding.ASCII.GetString(arr);
Console.WriteLine($"WHVl.decode=>{dest}");
//Xu=>WHU=
arr = Encoding.ASCII.GetBytes("Xu");
dest = Base64.Encode(arr);
Console.WriteLine($"Xu.base64=>{dest}");
//WHU==>Xu
arr = Base64.Decode("WHU=");
dest = Encoding.ASCII.GetString(arr);
Console.WriteLine($"WHU=.decode=>{dest}");
//X=>WA==
arr = Encoding.ASCII.GetBytes("X");
dest = Base64.Encode(arr);
Console.WriteLine($"X.base64=>{dest}");
//WA===>X
arr = Base64.Decode("WA==");
dest = Encoding.ASCII.GetString(arr);
Console.WriteLine($"WA==.decode=>{dest}");
//xiaoming.base64=>eGlhb21pbmc=
arr = Encoding.ASCII.GetBytes("xiaoming");
dest = Base64.Encode(arr);
Console.WriteLine($"xiaoming.base64=>{dest}");
//eGlhb21pbmc==>xiaoming
arr = Base64.Decode("eGlhb21pbmc=");
dest = Encoding.ASCII.GetString(arr);
Console.WriteLine($"eGlhb21pbmc=.decode=>{dest}");
//小明.utf8.base64=>5bCP5piO
arr = Encoding.UTF8.GetBytes("小明");
dest = Base64.Encode(arr);
Console.WriteLine($"小明.utf8.base64=>{dest}");
//5bCP5piO=>小明
arr = Base64.Decode("5bCP5piO");
dest = Encoding.UTF8.GetString(arr);
Console.WriteLine($"5bCP5piO.decode=>{dest}");
//小明45hu在哪里.utf8.base64=>5bCP5piONDVodeWcqOWTqumHjA==
arr = Encoding.UTF8.GetBytes("小明45hu在哪里");
dest = Base64.Encode(arr);
Console.WriteLine($"小明45hu在哪里.utf8.base64=>{dest}");
//5bCP5piONDVodeWcqOWTqumHjA===>小明45hu在哪里
arr = Base64.Decode("5bCP5piONDVodeWcqOWTqumHjA==");
dest = Encoding.UTF8.GetString(arr);
Console.WriteLine($"5bCP5piONDVodeWcqOWTqumHjA==.decode=>{dest}");
}
运行结果:
三、总结
以上代码只是试验的base64的编解码算法,实际应用中,可以直接使用系统自带的方法,如下:
//编码
Console.WriteLine(Convert.ToBase64String(Encoding.UTF8.GetBytes("小明45hu在哪里")));
//解码
Console.WriteLine(Encoding.UTF8.GetString(Convert.FromBase64String("5bCP5piONDVodeWcqOWTqumHjA==")));