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==")));
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jackletter

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值