base64編解碼原理及實現

原理參照: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==")));
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章