原理參照: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==")));