首页 > 其他分享 >Base64理解与实现

Base64理解与实现

时间:2022-12-13 16:55:05浏览次数:54  
标签:字符 字节 填充 编码 实现 Base64 理解 字符串

Base64理解与实现

(一)概述

Base64在日常开发中的出镜率还是比较高的,那你真的了解它吗?它是加密算法吗?它有什么作用?具体算法是怎么样的?为什么叫Base64?

(二)它是什么

Base64是一种二进制到文本的编码方式。可以认为它是一种将byte数组编码为字符串的方法。且编码出的字符串只包含ASCII基础字符。

例如字符串ShuSheng007对应的Base64为U2h1U2hlbmcwMDc=。其中“=”是填充符,稍后再说。

值得注意的是Base64不是加密算法,其仅仅是一种编码方式,算法也是公开的,不能依赖它进行加密。

(三)名称来历

它是一种基于(Base)64个字符的编码方式。用该算法编码后得到的文本只包含64种ASCII字符(偶尔加一个或两个填充字符“=”),如下所示:

Base64使用到的64个字符:

  • A-Z, 26个字符
  • a-z, 26个字符
  • 0-9, 10个字符
  • +, 1个字符
  • /, 1个字符

下图是Base64码表。可以看到从0到63的每个数字都对应一个上面的一个字符。

索引 对应字符 索引 对应字符 索引 对应字符 索引 对应字符
0 A 17 R 34 i 51 z
1 B 18 S 35 j 52 0
2 C 19 T 36 k 53 1
3 D 20 U 37 l 54 2
4 E 21 V 38 m 55 3
5 F 22 W 39 n 56 4
6 G 23 X 40 o 57 5
7 H 24 Y 41 p 58 6
8 I 25 Z 42 q 59 7
9 J 26 a 43 r 60 8
10 K 27 b 44 s 61 9
11 L 28 c 45 t 62 +
12 M 29 d 46 u 63 /
13 N 30 e 47 v
14 O 31 f 48 w
15 P 32 g 49 x
16 Q 33 h 50 y
Padding =

我的注释:每个Base64字符编码都为6bit。字节串被编码后呈现为ASCII字符串。ASCII字符串中的字符是8bit,比6bit多2bit,所以字节串被编码成Base64字符串后,体积会膨胀2/6约33%。编码后得到的ASCII字符串中,一个字符8bit,只能表示原来字节串中的6bit。

(四)解决的问题

这块还是比较难说清楚的,我们就抓住主要矛盾吧。假设王二狗给牛翠花通过邮件发情书:

亲爱的翠花我爱你,就像老鼠爱大米……你是我的心,你是我的肝,你是我生命中的四分之三……

首先要明白,网络中只能传输0和1,所以我们首先要将这封情书转成01序列。那怎么转呢? 这就又涉及到了编码方式,这块展开说能说两天。这里假设使用UTF-8转成的byte[], 值是我瞎写的

[100][11][1][13]....
// 每个方框里是一个十进制整数

这个byte[]里面的那些数字,传输时候是以二进制形式进行的,格式如下:

01100100 00001011 00000001 00001101...

当牛翠花收到这串数字后再将其按UTF-8解码,就能看到二狗的情书了。

若一切都这么顺利,就没Base64什么事了。由于信息从王二狗的电脑到牛翠花的电脑中间会经过好多路由器转发,牛翠花电脑上安装的邮件客户端也搞幺蛾子。其中一个叫阿路的路由器发现现在使用的是文本传输协议,而且有个byte的值是[13],而13在ASIIC码中对应回车键。阿路一想,这玩意也表达不出个什么意思,算了给他娘的省略了吧,为下面的兄弟省点宽带。阿路又看到一个byte的值为[07],又自作聪明给省略了,因为其在ASIIC中表示铃声。这样等到01比特序列到达牛翠花的电脑时已经不是王二狗发出时的样子了。好死不死的是,牛翠花用的是Mac系统。其邮件客户端也自作聪明,又处理了好多byte。最终导致使用UTF-8解码失败或者解出来的内容不对。

若是英文文本还好一点,因为被处理的都是那些ASCII中不可见字符。那些可见字符还是可以解码出来的。若是张图片,牛翠花可能就打不开了。

为了解决各系统及传输协议中的二进制不兼容,而产生了Base64。那么,为什么Base64就兼容呢?因为各方都能保证支持ASCII中的那些基础字符,于是就从那些基础字符中挑了64个,所以Base64能同时满足各方的需求。

目前由于传输导致的二进制改变已很少见了,各种系统对二进制的兼容性处理也越来越好。假如你告诉我这一串01是一张jpg格式的图片,那我就按照jpg算法将其恢复成一张jpg图片,所以大家都很高兴。

(五)应用场景

  • 证书。
  • 电子邮件附件(附件往往有不可见字符)。
  • 嵌套XML。若往XML中直接嵌入另一个XML会扰乱原本的XML,导致解析错误。因此,要把待插入的XML编码成字节数组,再编码成可见字符。
  • 以Base64编码方式把一些小图片嵌入网页,从而不用再链接请求消耗网络资源。
  • 较老的纯文本协议SMTP。使用SMTP传输文件时,需要用Base64。

(六)算法简介

这里只做简单介绍,详情请参考相关技术文档。

Base64进行编码步骤大概有四步:

  1. 将原始数据按每3个字节(1字节为8bit)为一组,故一组为24比特。
  2. 将24个bit分为四组,每组6个bit。
  3. 在每组前面加补00,将其补全成4个8bit。至此,原生数据的3个字节已经变成4个字节,增大约33%。
  4. 根据Base64码表得到扩展后每个字节的对应的字符(见上图)

我的注释: 6和8的最小公倍数为24。

下图是维基百科上面的一个例子。设原文为Man。下图演示了如何按上面的步骤将其编码为Base64字符串。

SourceText(ASCII)Man
Octets77(0x4d)97(0x61)110(0x6e)
Bits010011010110000101101110
Base64 EncodedSextets1922546
CharacterTWFu
Octets84(0x54)87(0x57)70(0x46)117(0x75)

可以发现Man对应的Base64为TWFu。现在应该明白为什么只有64个字符了吧?因为算法将8bit分割成了6bit,而6bit的取值范围为0~63。

(七)填充字符

有时会在Base64字符串末尾出现“=”;有时1个,有时2个。为什么?

通过上面的讲解,我们知道了Base64编码过程是将3个字符分为一组的进行。如果原文长度不是3的倍数,那该怎么办呢?例如,原文为“Ma”,不够3个,那么只能在编码后的字符串中补“=”了。缺1个字符补1个,缺2个,补2个即可。所以有时候你会看到Base64字符串结尾有1个或者2个“=”。

SourceText(ASCII)Ma
Octets77(0x4d)97(0x61)
Bits010011010110000100------
Base64 EncodedSextets19224Padding
CharacterTWE=
Octets84(0x54)87(0x57)69(0x45)61(0x3D)

Ma为两个字节,2 / 3 = 0 余 2,补3-2=1个字符。

若字节长度L除3的余数为R,那么,

  1. 当R为0时,需要补(填充)0个字符,即(3-0)个。
  2. 当R为1时,需要补(填充)2个字符,即(3-1)个。
  3. 当R为2时,需要补(填充)1个字符,即(3-2)个。

(八)DataURI中的Base64

有时,你会发现Web页面传给来的Base64字符串前面有类似下面这样的符号:

data:image/jpeg;base64, /9j/4AA...

这是DataURI。大部分浏览器都支持直接打开这类二进制数据。但是我们要格外注意,若你只是想要真实的Base64内容,就需要取“,”后面的内容。

(九)总结

  1. Q:Base64算法是加密算法吗?A:不是,而是一种编码方式。
  2. Q:Base64算法的作用是什么?A:将字节数组编码为只含ASCII基础字符的字符串。
  3. Q:Base64算法是怎么实现的?A:见文中的算法简介小节。
  4. Q:Base64的名称是怎么来的?A:因为它是一种基于(Base)64个ASCII基础字符编码方式。
  5. Q:Base64编码解决什么问题?A:解决各系统及传输协议中二进制不兼容的问题。
  6. Q:Base64字符串末尾的填充字符“=”的作用是什么?待编码字节数组按照3个字节(共24比特)为一组进行编码。每组(24比特)被编码成4个字符(共32比特)。若待编码字节数组最后一个分组不够凑齐3字节,则要进行填充。缺1个字节,则在最终Base64字符串中填充1个填充字符“=”;缺2个字节,则填充2个填充字符“=”。

(十)实战

1.编码解码Base64串(C#)

// Base64编码
string plainText = "TestString";
byte[] plainTextBytes = System.Text.Encoding.UTF8.GetBytes(message);
string base64EncodedText = System.Convert.ToBase64String(plainTextBytes)
Console.WriteLine(base64EncodedText);

// Base64解码
byte[] base64DecodedBytes = System.Convert.FromBase64String(base64EncodedText);
string originalPlainText = System.Text.Encoding.UTF8.GetString(base64DecodedBytes)
Console.WriteLine(originalPlainText);

2.Base64算法实现(C#)

// Converter.cs

using System;
using System.Text;
using System.Collections.Generic;

namespace Kokiafan.Text
{
    /// <summary>
    /// 转换类。
    /// </summary>
    public class Converter
    {
        #region 编解码相关常数
        /// <summary>
        /// Base64字符编码表(编码->字符)。
        /// </summary>
        private static char[] codeTable = new char[] {
            'A','B','C','D','E','F','G','H',
            'I','J','K','L','M','N','O','P',
            'Q','R','S','T','U','V','W','X',
            'Y','Z','a','b','c','d','e','f',
            'g','h','i','j','k','l','m','n',
            'o','p','q','r','s','t','u','v',
            'w','x','y','z','0','1','2','3',
            '4','5','6','7','8','9','+','/',
        };
        /// <summary>
        /// Base64编码表(字符->编码)
        /// </summary>
        private static Dictionary<char, uint> codeDictionary = new Dictionary<char, uint> {
            {'A', 0 },{'B', 1 },{'C', 2 },{'D', 3 },{'E', 4 },{'F', 5 },{'G', 6 },{'H', 7 },
            {'I', 8 },{'J', 9 },{'K', 10 },{'L', 11 },{'M', 12 },{'N', 13 },{'O', 14 },{'P', 15 },
            {'Q', 16 },{'R', 17 },{'S', 18 },{'T', 19 },{'U', 20 },{'V', 21 },{'W', 22 },{'X', 23 },
            {'Y', 24 },{'Z', 25 },{'a', 26 },{'b', 27 },{'c', 28 },{'d', 29 },{'e', 30 },{'f', 31 },
            {'g', 32 },{'h', 33 },{'i', 34 },{'j', 35 },{'k', 36 },{'l', 37 },{'m', 38 },{'n', 39 },
            {'o', 40 },{'p', 41 },{'q', 42 },{'r', 43 },{'s', 44 },{'t', 45 },{'u', 46 },{'v', 47 },
            {'w', 48 },{'x', 49 },{'y', 50 },{'z', 51 },{'0', 52 },{'1', 53 },{'2', 54 },{'3', 55 },
            {'4', 56 },{'5', 57 },{'6', 58 },{'7', 59 },{'8', 60 },{'9', 61 },{'+', 62 },{'/', 63 },
            {'=',0 },
        };
        /// <summary>
        /// 掩码数组,分别用于取32位无符号整数bit31-bit26,bit25-bit20,
        /// bit19-bit14和bit13-bit08各分组的6bit值。一共24bits。
        /// </summary>
        private static uint[] masks = new uint[] {
            0b11111100_00000000_00000000_00000000,
            0b00000011_11110000_00000000_00000000,
            0b00000000_00001111_11000000_00000000,
            0b00000000_00000000_00111111_00000000
        };
        private static uint[] masks8 = new uint[]
        {
            0b11111111_00000000_00000000_00000000,
            0b00000000_11111111_00000000_00000000,
            0b00000000_00000000_11111111_00000000,
        };
        #endregion

        #region Base64编解码公开方法。
        /// <summary>
        /// 将8位无符号整数的数组转换为其用Base64数字编码的等效字符串表示形式。
        /// 
        /// 异常:
        /// ArgumentNullException:inArray参数为null。
        /// 
        /// 作者:Kokiafan
        /// 日期:2022-11-06
        /// 版本:1.0.0.0
        /// </summary>
        /// <param name="inArray">待转换的无符号整型字节数组。</param>
        /// <returns>Base64编码字符串。</returns>
        public static string ToBase64String(byte[] inArray)
        {
            // 输入参数检查
            if (inArray == null)
                throw new ArgumentNullException("Parameter 'inArray' should not be null.");
            if (inArray.Length <= 0)
                return "";

            /*
             * 一边遍历一边合并。遍历的过程中,将字节流中的字节tempByte移位相应的位数,
             * 再与结果的32位无符号整型tripleByte取“或”位运算。在将输入字节流分成3字节
             * 一组后,每个组的第0字节需要向左移动8*(3-0)=24位;第1字节需要向左移动
             * 8*(3-1)=16位;第2字节需要向左移动8*(3-2)=8位。
             * 
             * 设遍历字节流inArray时的索引为i,i除3的余数为remainder。在遍历的过程中,
             * remainder的值随着i的变化按0,1,2,0,1,2...这样呈周期性变化。
             * 当remainder为0时,此时处理的是“3字节分组”中的第0字节;
             * 当remainder为1时,此时处理的是“3字节分组”中的第1字节;
             * 当remainder为2时,此时处理的时“3字节分组”中的第2字节。
             * 处理完“3字节分组”中的第2字节后,本次3字节分组合并为“24比特”tripleByte的操
             * 作就已完成。需进入“将24位比特编码为4字符Base64字符串”的操作。为此设置标识
             * 变量full,初始时为false,当remainder为2时,full置true。进入下一次遍历(或
             * 退出遍历后)再进入“将24位比特编码为4字符Base64字符串”的操作。然后将编码得到
             * 的Base64字符串放入缓存StringBuilder类实例。重置tripleByte的所有比特,full
             * 置false。
             * 
             * 退出遍历后,检查full是否为true。true,表示最后一个分组被处理完且有3个字节,故
             * 进入“将24位比特编码为4字符Base64字符串”的操作。false,表示最后一个分组被处理
             * 完,且不足3个字节。那么差多少字节?remainder为0时,差2个字节;remainder为1时,
             * 差1个字节。总结起来就是差2-remainder个字节,需补2-remainder个填充符(“=”)。
             */
            bool full = false;
            int remainder = 0;
            uint tripleByte = 0, tempByte = 0;
            StringBuilder sb = new StringBuilder();
            
            for (int i = 0; i < inArray.Length; i++)
            {
                remainder = i % 3;

                if (full == true)
                {
                    sb.Append(GetBase64(tripleByte));
                    // 用与自己进行异或(XOR)计算的方式来清零。
                    tripleByte = tripleByte ^ tripleByte;
                    full = false;
                }

                tempByte = inArray[i];
                tripleByte = tripleByte | (tempByte << 8 * (3 - remainder));

                /* 
                 * 若remainder为2,则本次“3字节分组”已合并为24bit。进入编码24bit为
                 * 4字符Base64字符串的阶段。
                 */
                if (remainder == 2)
                    full = true;
            }

            //if (full == true)
            //{
            //    /*
            //     * r的取值范围[0-2],
            //     * 2时已处理3个字节;1时已处理2个字节;
            //     * 0时已处理1个字节。
            //     */
            //    sb.Append(GetBase64(tripleByte, 2 - remainder));
            //    tripleByte = tripleByte ^ tripleByte;
            //    full = false;
            //}
            //else
            //{
            //    sb.Append(GetBase64(tripleByte, 2 - remainder));
            //}
            sb.Append(GetBase64(tripleByte, 2 - remainder));
            return sb.ToString();
        }
        /// <summary>
        /// 将指定的字符串(它将二进制数据编码为Base64数字)转换为等效的8位
        /// 无符号整数数组。
        /// 
        /// 异常:
        /// ArgumentNullException:s上声明的默认值为null。
        /// FormatException:s的长度(忽略空格)不是0或4的倍数。
        ///  
        /// 作者:Kokiafan
        /// 日期:2022-11-06
        /// 版本:1.0.0.0
        /// </summary>
        /// <param name="s">要转换的字符串。</param>
        /// <returns>与s等效的8位无符号整数数组。</returns>
        public static byte[] FromBase64String(string s)
        {
            // 输入参数检查
            if (s is null)
                throw new ArgumentNullException("Parameter 's' should not be null.");
            if ((s.Length != 0) && ((s.Length % 4) != 0))
                throw new FormatException("The length of s should be 0 or multiple of 4.");

            if (s.Length == 0)
                return new byte[] { };
            // 存放解码字节的缓存。
            List<byte> bytes = new List<byte>();
            /* 已知输入的Base64字符串的长度要么是0,要么是4的整数被。故可以安全地以一次处
             * 理4个字符的方式进行解码。 
             */
            for (int i = 0; i < s.Length; i += 4)
            {
                bytes.AddRange(FromBase64Chars4(s[i + 0], s[i + 1], s[i + 2], s[i + 3]));
            }

            return bytes.ToArray();
        }
        #endregion

        #region 与编码方法相关的辅助私有方法。
        /// <summary>
        /// 将24位比特(实际为从32bit无符号整型的最高位bit31到bit8的24bits),编
        /// 码为4个字符的Base64字符串。默认情况下,填充0个字符。
        /// 
        /// 异常:
        /// ArgumentOutOfRangeException:填充字符“=”数超过范围[0,2]。
        /// 
        /// 作者:Kokiafan
        /// 日期:2022-11-06
        /// 版本:1.0.0.0
        /// </summary>
        /// <param name="tripleByte">待编码的24位比特。</param>
        /// <param name="numberOfPaddings">填充字符“=”的数量。取值范围为[0, 2]。</param>
        /// <returns>编码后的Base64字符串(含4个字符)。</returns>
        private static string GetBase64(uint tripleByte, int numberOfPaddings = 0)
        {
            // 输入参数检查
            // numberOfPaddings的取值范围[0-2]
            // 2:已处理3个字节,无需填充(Padding);
            // 1:已处理2个字节,需填充一个'='字符;
            // 0:已处理1个字节,需填充两个'='字符。
            if (numberOfPaddings < 0 || numberOfPaddings > 2)
                throw new ArgumentOutOfRangeException("numberOfPaddings",
                    "numberOfPaddings ∈ [0, 2]");

            // 将含有4个字符的数组中的每个字符都初始化位填充符“=”。这样可以免去编码后
            // 还要再单独对编码串进行填充处理的麻烦。
            char[] base64Chars = new char[] { '=', '=', '=', '=' };
            // 这里的迭代条件是i < 4 - numberOfPaddings。这样就不需要对编码结果另外
            // 再进行行填充操作。用预先准备好的掩码与输入的32位无符号整型做“与”运算,
            // 将运算结果向右移位,以计算对应Base64字符的编码。
            // 第一个6bits位于bit31-bit26,需被往右移动26bits。
            // 第二个6bits位于bit25-bit20,需被往右移动20bits。
            // 第三个6bits位于bit19-bit14,需被往右移动14bits。
            // 第四个6bits位于bit13-bit08,需被往右移动8bits。
            // 最后将编码译成Base64字符。
            for (int i = 0; i < 4 - numberOfPaddings; i++)
            {
                base64Chars[i] = GetBase64Char(Convert.ToInt32(
                    (tripleByte & masks[i]) >> 32 - 6 * (i + 1)));
            }
            /*
             * 把字符数组转换成字符串的三种方法:
             * 方法一:string类的构造方法
             * string str = new string(charArray);
             * 方法二:用Concat函数串联集合内可枚举接口实现的成员
             * string str = string.Concat<char>(charArray);
             * 方法三:用分隔符串联集合内的成员
             * string str = string.Join("", charArray);
             */
            return new string(base64Chars);
        }
        /// <summary>
        /// 根据编码值获取对应的Base64字符。
        /// 
        /// 作者:Kokiafan
        /// 日期:2022-11-06
        /// 版本:1.0.0.0
        /// </summary>
        /// <param name="index">Base64字符的编码值。</param>
        /// <returns>对应的Base64字符。</returns>
        private static char GetBase64Char(int index)
        {
            return codeTable[index];
        }
        #endregion

        #region  与解码方法相关的辅助私有方法。
        /// <summary>
        /// 给定4个连续的Base64字符,将它们翻译回24位比特流。例如:
        /// Base64串“TWFu”译为“01001101_01100001_01101110”。其
        /// 中,'T'对应参数c0,'W'对应参数c1,'F'对应参数c2,'u'对
        /// 应参数c3。
        /// 
        /// 异常:
        /// ArgumentException:c2为填充字符“=”,而c3不是填充字符。
        /// 
        /// 作者:Kokiafan
        /// 日期:2022-11-06
        /// 版本:1.0.0.0
        /// </summary>
        /// <param name="c0">4字节Base64串中的第0个字符。</param>
        /// <param name="c1">4字节Base64串中的第1个字符。</param>
        /// <param name="c2">4字节Base64串中的第2个字符。</param>
        /// <param name="c3">4字节Base64串中的第3个字符。</param>
        /// <returns>4个Base64字符所表示的24位比特流。</returns>
        private static byte[] FromBase64Chars4(char c0, char c1, char c2, char c3)
        {
            // 输入参数检查
            /*
             * 4字符Base64字符串分组中,c2为填充符“=”而c3不是填充符,那它必然不是合法
             * Base64字符串。
             */
            if (c2 == '=' && c3 != '=')
                throw new ArgumentException("原字符串不是有效的Base64字符串。(c2为=,而c3不为=)");
            // 默认情况下填充字符的数量为0。
            int numberOfPaddings = 0;
            // 填充数
            if (c2 == '=' && c3 == '=')
                numberOfPaddings = 2;
            else if (c3 == '=')
                numberOfPaddings = 1;
            /*
             * 把第0字符解码为对应的编码,并将低位的6bit移到bit31-bit26。
             * 把第1字符解码为对应的编码,并将低位的6bit移到bit25-bit20。
             * 把第2字符解码为对应的编码,并将低位的6bit移到bit19-bit14。
             * 把第3字符解码为对应的编码,并将低位的6bit移到bit13-bit8。
             */
            uint b0 = GetBase64Value(c0) << 26;
            uint b1 = GetBase64Value(c1) << 20;
            uint b2 = GetBase64Value(c2) << 14;
            uint b3 = GetBase64Value(c3) << 8;
            uint bit32 = 0;
            /*
             * 将得到的4个6bit合并为一个24bit+8bit=32bit(低位的8个bit不用)。
             */
            bit32 = bit32 | b0;
            bit32 = bit32 | b1;
            bit32 = bit32 | b2;
            bit32 = bit32 | b3;
            /*
             * 把bit31-bit8共24bit拆分成3个字节。放入字节数组bytes。
             */
            byte[] bytes = new byte[] { 0, 0, 0 };
            bytes[0] = System.Convert.ToByte((bit32 & masks8[0]) >> 24);
            bytes[1] = System.Convert.ToByte((bit32 & masks8[1]) >> 16);
            bytes[2] = System.Convert.ToByte((bit32 & masks8[2]) >> 8);
            /* 
             * 根据原Base64字符串中的填充符数量,对4字节的结果进行裁切。
             * 2个填充符,则最终结果为3-2=1个字节;
             * 1个填充符,则最终结果为3-1=2个字节;
             * 0个填充符,则最终结果为3-0=3个字节。
             */
            int length = 3 - numberOfPaddings;
            //byte[] result = new byte[length];
            //Array.Copy(bytes, 0, result, 0, length);
            //return result;
            return bytes.Take(length).ToArray();
        }
        /// <summary>
        /// 通过查编码表的方式返回指定Base64字符的编码,并以32位
        /// 无符号数字的方式返回。
        ///  
        /// 作者:Kokiafan
        /// 日期:2022-11-06
        /// 版本:1.0.0.0 
        /// </summary>
        /// <param name="base64Char">待解码的Base64字符。</param>
        /// <returns>对应字符的Base64编码。</returns>
        private static uint GetBase64Value(char base64Char)
        {
            return codeDictionary[base64Char];
        }
        #endregion
    }
}

3.使用Base64算法实现

// Program.cs
using Kokiafan.Text;
using System;

namespace EncodeBase64
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] messages = new string[] {
                "man",
                "tobase64string(byte[])",
                "将 8 位无符号整数的数组转换为其用 base64 数字编码的等效字符串表示形式。",
                "下面的示例使用 tobase64string(byte[]) 此方法将字节数组转换为 uuencoded (base-64) 字符串,然后调用 frombase64string(string) 该方法来还原原始字节数组。",
            };

            for (int i = 0; i < messages.Length; i++)
            {
                Console.WriteLine($"origin: {messages[i]}");
                byte[] textbytes = System.Text.Encoding.UTF8.GetBytes(messages[i]);
                string myBase64String = Converter.ToBase64String(textbytes);
                string msBase64String = System.Convert.ToBase64String(textbytes);
                Console.WriteLine($"mybase64:{myBase64String}");
                Console.WriteLine($"msbase64:{msBase64String}");
                byte[] fromBase64Bytes = Converter.FromBase64String(myBase64String);
                Console.WriteLine($"decode: {System.Text.Encoding.UTF8.GetString(fromBase64Bytes)}");
                Console.WriteLine();
            }
        }
    }
}

// Program.cs的运行结果

origin: man
mybase64:bWFu
msbase64:bWFu
decode: man

origin: tobase64string(byte[])
mybase64:dG9iYXNlNjRzdHJpbmcoYnl0ZVtdKQ==
msbase64:dG9iYXNlNjRzdHJpbmcoYnl0ZVtdKQ==
decode: tobase64string(byte[])

origin: 将 8 位无符号整数的数组转换为其用 base64 数字编码的等效字符串表示形式。
mybase64:5bCGIDgg5L2N5peg56ym5Y+35pW05pWw55qE5pWw57uE6L2s5o2i5Li65YW255SoIGJhc2U2NCDmlbDlrZfnvJbnoIHnmoTnrYnmlYjlrZfnrKbkuLLooajnpLrlvaLlvI/jgII=
msbase64:5bCGIDgg5L2N5peg56ym5Y+35pW05pWw55qE5pWw57uE6L2s5o2i5Li65YW255SoIGJhc2U2NCDmlbDlrZfnvJbnoIHnmoTnrYnmlYjlrZfnrKbkuLLooajnpLrlvaLlvI/jgII=
decode: 将 8 位无符号整数的数组转换为其用 base64 数字编码的等效字符串表示形式。

origin: 下面的示例使用 tobase64string(byte[]) 此方法将字节数组转换为 uuencoded (base-64) 字符串,然后调用 frombase64string(string) 该方法来还原原始字节数组。
mybase64:5LiL6Z2i55qE56S65L6L5L2/55SoIHRvYmFzZTY0c3RyaW5nKGJ5dGVbXSkg5q2k5pa55rOV5bCG5a2X6IqC5pWw57uE6L2s5o2i5Li6IHV1ZW5jb2RlZCAoYmFzZS02NCkg5a2X56ym5Liy77yM54S25ZCO6LCD55SoIGZyb21iYXNlNjRzdHJpbmcoc3RyaW5nKSDor6Xmlrnms5XmnaXov5jljp/ljp/lp4vlrZfoioLmlbDnu4TjgII=
msbase64:5LiL6Z2i55qE56S65L6L5L2/55SoIHRvYmFzZTY0c3RyaW5nKGJ5dGVbXSkg5q2k5pa55rOV5bCG5a2X6IqC5pWw57uE6L2s5o2i5Li6IHV1ZW5jb2RlZCAoYmFzZS02NCkg5a2X56ym5Liy77yM54S25ZCO6LCD55SoIGZyb21iYXNlNjRzdHJpbmcoc3RyaW5nKSDor6Xmlrnms5XmnaXov5jljp/ljp/lp4vlrZfoioLmlbDnu4TjgII=
decode: 下面的示例使用 tobase64string(byte[]) 此方法将字节数组转换为 uuencoded (base-64) 字符串,然后调用 frombase64string(string) 该方法来还原原始字节数组。

(十一)参考资料

Base64 Encode And Decode In C#

Convert.ToBase64String 方法 (System) | Microsoft Learn

原文链接:让你彻底理解Base64算法

标签:字符,字节,填充,编码,实现,Base64,理解,字符串
From: https://www.cnblogs.com/kokiafan/p/16979268.html

相关文章

  • C++有关class内部的static关键字理解
    变量在class中被static修饰的成员变量是可以被直接访问的,不需要实例化。并且所有实例共享同一份该变量,进而可实现单例模式。如果换个理解方式,class仅提供一个namespace......
  • 理解ADC微分非线性(DNL)误差
    1,理解ADC微分非线性(DNL)误差2,UnderstandingADCDifferentialNonlinearity(DNL)Error......
  • js 实现退拽效果
    单个元素拖拽,不随页面滚动css部分body{height:2000px;}div{width:150px;height:150px;cursor:move;position:fixed;top:50px;color:#fff;box-sizing:border-box;z......
  • [ Linux ] 一篇带你理解Linux下线程概念
    1.Linux线程的概念1.1什么是线程在之前我们谈过Linux的进程,每一个进程都有自己的PCB,和自己的进程地址空间。地址空间和物理内存通过页表建立映射。那么现在我要创建一个新的......
  • Android 手势导航核心实现
    一、如何找到入口Android10推出了全新的手势导航功能,原生的Android系统就提供了此功能,根据这个切入点查询相关实现,Android10和11的源码里面,在SystemUI模块里面可以找到......
  • Unity UGUI实现图文混排
    目前在unity实现图文混排的好像都是通过自定义字体然后在文本获取字符的位置,用图片替换掉图片标签,这样对于支持英文来说,并没有什么影响。然后对于中文来说就是一个相当麻烦......
  • Unity实现在白板上绘画涂鸦
    前言有段时间没有更新博客了,不知道应该写些什么,太简单感觉没有记录的必要,太难自己都没能理解,不知道如何下手。回归初心,记录自己想记录的东西。需要实现一个白板绘画的功能,......
  • 通过代码实现Excel中数据插入数据库
    packagecom.tianju.supermelon.controller;importcom.tianju.supermelon.common.dtos.ResponseResult;importcom.tianju.supermelon.domain.AvatarInfo;importcom.tian......
  • UE4实现闪烁效果
    官网文档链接:​​http://docs.unrealengine.com/latest/CHN/Engine/Rendering/Materials/ExpressionReference/Math/index.html?utm_source=editor&utm_medium=docs&utm_ca......
  • Unity UGUI实现分段式血条
    我们可以看到像英雄联盟等游戏里英雄头顶的血条显示并非是纯色的,而是根据血量的多少而显示一定量的格子,这种方式明显是比较友好、比较美观的,事实上我们的游戏里面也想实现这......