美文网首页
编码原理理解之「 base64」

编码原理理解之「 base64」

作者: Chriszzzz | 来源:发表于2020-09-28 15:44 被阅读0次

    一、什么是 base64

    Base64 是网络上最常见的用于传输 8Bit 字节码的编码方式之一,是一种基于 64 个可打印字符(见下图👇)来表示 二进制数据 的方法。

    标准base64编码对应表

    也就是说,经过 base64 转换过后的二进制数据,其只由 65 个字符(上述 64个👆 + 末尾可能填充的”=“)组成。👇

    Base64 是网络上最常见的用于传输8Bit字节码的编码方式之一,Base64就是一种基于64个可打印字符来表示二进制数据的方法。
    .
    == base64编码后 ==>
    .
    QmFzZTY0IOaYr+e9kee7nOS4iuacgOW4uOingeeahOeUqOS6juS8oOi+kzhCaXTlrZfoioLnoIHnmoTnvJbnoIHmlrnlvI/kuYvkuIDvvIxCYXNlNjTlsLHmmK/kuIDnp43ln7rkuo42NOS4quWPr+aJk+WNsOWtl+espuadpeihqOekuuS6jOi/m+WItuaVsOaNrueahOaWueazleOAgg==

    对吧,找不到第 66 个字符。

    但为什么会有 base64 这么奇怪的编码方式呢?

    邮件传输协议(SMTP)是不支持不可见字符的传递的(不可见字符比如换行符、制表符等)。但是图片的数据所表示的字符中,存在大量的不可见字符,那么,邮件就不能传输图片么?显然不可接受。为了解决这个问题,大牛们提出了 base64 编码,传输时通过 base64 将任何二进制数据都用可见字符来表示。

    如今,base64 也被应用在 X.509 公钥证书约定中,HTTP请求的参数编码上,XML结构中存储二进制数据的场景中。简而言之,基于数据传输、存储,屏蔽了大量的不可见字符(他们往往有特殊含义),传输,存储的可靠性大幅提升。

    二、base64 的编码规则

    base64是对二进制数据进行编码的!base64是对二进制数据进行编码的!base64是对二进制数据进行编码的! 此点不同于我们常见的 utf-8 编码(做「字符串」&「二进制」之间的转换),base64 做的是「二进制」 & 「能转化为可见字符的二进制」之间的转换。

    简单来讲,base64 编码分为如下几步:
    1)将二进制数据按照 3字节(24位)分组;
    2)将每组数据分为 4份(每份6位)
    3)每份数据 (6位能表示0~63) 查找 base64 编码对应表,找到对应字符;
    4)将对应字符基于 ASCII 表示,最后纯空的份用 ’=‘ 替代。

    三、base64 的编码举例

    比如我们想对 ”今儿个真Happy!“ 的 utf8(你当然可以用其他的编码方式转换,毕竟 base64 只关心二进制输入)的二进制数据进行编码。

    Step1:获取待编码的二进制数据

    ”今儿个真Happy!“,经过 utf-8 编码的二进制数据如下👇

    今儿个真Happy!
    
    == utf-8编码 ==>
    
    十六进制表示:e4 bb 8a e5 84 bf e4 b8 aa e7 9c 9f 48 61 70 70 79 ef bc 81 
    二进制表示:11100100 10111011 10001010 11100101 10000100 10111111 11100100 10111000 10101010 11100111 10011100 10011111 01001000 01100001 01110000 01110000 01111001 11101111 10111100 10000001
    

    Step2:将二进制数据分组

    接下来,我们需要将待编码的二进制数据按照 3字节 一组分组。因为 1字节8位base6464 个可见字符需要用 6位(2的6次方)表示,86 的最小公倍数是 24,即 3 个字节。

    11100100 10111011 10001010 11100101 10000100 10111111 11100100 10111000 10101010 11100111 10011100 10011111 01001000 01100001 01110000 01110000 01111001 11101111 10111100 10000001
    
    == 3字节一组 ==>
    
    11100100 10111011 10001010 
    11100101 10000100 10111111 
    11100100 10111000 10101010 
    11100111 10011100 10011111 
    01001000 01100001 01110000 
    01110000 01111001 11101111 
    10111100 10000001 
    

    但我们发现原始二进制的字节数无法被3整除,没关系,我们后面再处理。

    Step3:每组做4等分

    我们将每组数据进行 4 等分(每份 6 位),基于 6 位得到 0 ~ 63 的值,即为 base64 编码的索引。

    11100100 10111011 10001010 
    11100101 10000100 10111111 
    11100100 10111000 10101010 
    11100111 10011100 10011111 
    01001000 01100001 01110000 
    01110000 01111001 11101111 
    10111100 10000001 
    
    == 4等分 ==>
    
    111001 001011 101110 001010 
    111001 011000 010010 111111 
    111001 001011 100010 101010 
    111001 111001 110010 011111 
    010010 000110 000101 110000 
    011100 000111 100111 101111 
    101111 001000 0001 
    
    最后一行非纯空的数据补 0 补满 6 位,纯空数据当做 '=':
    101111 001000 0001  ==> 101111 001000 000100 '='
    
    == 每份二进制转为10进制 ==》
    
    05 11 20 10 
    05 24 18 63 
    05 11 08 16 
    05 05 24 05 
    18 06 05 22 
    02 07 13 21 
    21 08 04 ’=‘
    

    Step4:做Base64的字符映射

    05 11 20 10 
    05 24 18 63 
    05 11 08 16 
    05 05 24 05 
    18 06 05 22 
    02 07 13 21 
    21 08 04 ’=‘
    
    == base64 字符映射 ==>
    
    5 L u K 
    5 Y S /
    5 L I q
    5 5 y f
    S G F w
    c H n v
    v I E = 
    
    对应 [ASCII](https://baike.baidu.com/item/ASCII/309296?fr=aladdin) 的二进制数据即:
    
    00110101 01001100 01110101 01001011 
    00110101 01011001 01010011 00101111 
    00110101 01001100 01101001 01110001 
    00110101 00110101 01111001 01100110 
    01010011 01000111 01000110 01110111 
    01100011 01001000 01101110 01110110 
    01110110 01001001 01000101 00111101 
    
    这即为base64完成编码后在内存中的值👆
    
    

    四、讨论

    4.1 网上常见的base64算法过程的补两个0问题

    网上很多关于 base64 的说明教程(包括百度百科)都描述了 3字节(3x8位)4x6位 后,在6位 数据前 补两个’0‘凑4组8位数据 的问题。而这个步骤存在的意义实质上在于方便 base64 标准代码实现的理解。在此,我们不分析 base64 编码的代码实现,所以小编未将该步骤加入其中。

    百度百科的组转换示例

    4.2 base64的索引存在与设计层,其存储层使用的是字符编码。

    第二个很让人困惑的问题就是 base64 转换后的二进制数据,如果你将 base64 转换的二进制数据打印出来,会发现每个字节的数据并不仅仅在 0 ~ 63 这个范围内。

    这是因为,base64 的实际存储形式并非其索引值……

    ABCD base64的映射

    比如一份转化后的 base64 数据映射到 ABCD,则其二进制表示并不是0x01020304,而是 ABCD 对应ASCII标中的值:0x41424344

    ABCD ASCII的映射

    base64 的设计是不关心转换的 base64 字符是如何存储的。但实际应用中,ASCII,或讲完全兼容 ASCIIutf-8 实在是太通用了,所以我们直接将 base64 与其结合,其实无可厚非。无论是苹果官方的 base64 编码,还是大家用得比较多的也比较老牌的 GTMBase64,都是这么做的。

    iOS自带base64编码接口说明

    4.3 base64的变种

    即便 base64 只使用了除等号 ”=“ 之外的64个字符,但它还是不出意外地会在各种场景下出问题……关键在于 62号 字符 "+",和 63号 字符 "/",它们在某些场景下是有特殊含义的。所以,我们也会在必要的场景将 ”+“”/“ 两个字符替换,组成变种的 base64 编码,比如:

    变种1:URL变种:"+","/","=" --> "-","_",""

    URL编码器会把标准 Base64 中的 “/”“+” 字符变为形如 “%XX” 的形式,而这些 “%” 号在存入数据库时还需要再进行转换,因为 ANSI SQL 中已将 “%” 号用作通配符。为解决此问题,可采用一种用于 URL 的改进 Base64 编码,它不仅在末尾去掉填充的 '=' 号,并将标准 Base64 中的 “+”“/” 分别改成了 “-”“_”,这样就免去了在 URL编解码和数据库存储时所要作的转换,避免了编码信息长度在此过程中的增加,并统一了数据库、表单等处对象标识符的格式。

    变种2:正则变种:"+","/" --> "!","-"
    另有一种用于正则表达式的改进 Base64 变种,它将 “+”“/” 改成了 “!”“-”,因为 “+” , “/”在正则表达式中具有特殊含义。

    五、实践

    用一行代码来打印 base64 的编码流程吧~

    /* 代码示例 */
    cytLogStringData_base64_flow(@"今儿个真Happy!");
    
    /* 打印流程 */
    1 输入字符串: 
    今儿个真Happy!
    
    2 将字符串转为数据(通过UTF-8)编码:
    16进制数据表示: e4 bb 8a e5 84 bf e4 b8 aa e7 9c 9f 48 61 70 70 79 ef bc 81 
    2进制数据表示: 11100100 10111011 10001010 11100101 10000100 10111111 11100100 10111000 10101010 11100111 10011100 10011111 01001000 01100001 01110000 01110000 01111001 11101111 10111100 10000001 
    
    3 将2进制数据进行3字节分组:
    11100100 10111011 10001010 
    11100101 10000100 10111111 
    11100100 10111000 10101010 
    11100111 10011100 10011111 
    01001000 01100001 01110000 
    01110000 01111001 11101111 
    10111100 10000001 
    
    4 将2进制数据每3个字节分为4组:
    111001 001011 101110 001010 
    111001 011000 010010 111111 
    111001 001011 100010 101010 
    111001 111001 110010 011111 
    010010 000110 000101 110000 
    011100 000111 100111 101111 
    101111 001000 0001
    
    5 获取base64编码的索引值:
    57 11 46 10 57 24 18 63 57 11 34 42 57 57 50 31 18 06 05 48 28 07 39 47 47 08 04 00 
    
    6 将索引映射为base64目标字符:
    5LuK5YS/5Liq55yfSGFwcHnvvIE=
    
    7 base64字符串内存中的样子(对应字符的ASCII值,或说utf-8编码值):
    00110101 01001100 01110101 01001011 00110101 01011001 01010011 00101111 00110101 01001100 01101001 01110001 00110101 00110101 01111001 01100110 01010011 01000111 01000110 01110111 01100011 01001000 01101110 01110110 01110110 01001001 01000101 00111101 
    

    git地址:https://github.com/chrisYooh/CYEncoding.git
    对你有帮助的话记得帮小编点个 「Star」 哦!✨✨✨

    相关文章

      网友评论

          本文标题:编码原理理解之「 base64」

          本文链接:https://www.haomeiwen.com/subject/pmowyktx.html