Skip to content

Latest commit

 

History

History
195 lines (135 loc) · 7.52 KB

base64.md

File metadata and controls

195 lines (135 loc) · 7.52 KB

目录

base64

原始字符串:

Hello World!

base64 编码后:

SGVsbG8gV29ybGQh

比例: 转换后 / 转换前 = 16 / 12 = 4 / 3

假设转换前是 ASCII 编码

在线转换工具

base64 允许的字符

base64 只允许出现以下字符,所有原始内容都要通过一定规则转换为下列字符:

  • a-z
  • A-Z
  • 0-9
  • 2 个特殊字符:+/
  • 1 个末尾补全字符:=

数量:26 个英文字母 * 2 + 2 个特殊字符 = 64(即 base64 中的 64

若算上补全字符 = 应该是 65 个允许的字符

base64 编码规则

先明确 base64 的输入、输出:

  • 输入:一个图片文件、一份 email 的内容、一段文本 等

    ↑ 本质是任意二进制内容

  • 输出:base64 字符串

    ↑ 本质是 ASCII 字符串

编码规则:

  • 首先,上述 64 个字符对应 64 个可能的情况,也即 64 = 2^6 = 6 个二进制位

    • 即 6 个二进制位就可表示上述 64 种情况
    • 如:二进制 010101 = 十进制 21 = base64 表中 V 字符
  • 步骤,参考阮一峰老师

    • 第 1 步,将每 3 个字节作为 1 组,一共是 24 个二进制位
    • 第 2 步,将这 24 个二进制位切为 4 组,每个组有 6 个二进制位
    • 第 3 步,在每组前面加 2 个 00,扩展成 32 个二进制位,即 4 个字节
    • 第 4 步,根据 base64 表,得到扩展后的每个字节的对应符号,这就是 base64 的编码值
  • 之后:将前面补全了 00 的 8 个二进制位转换为 ASCII 编码,举例:

    • 原始 6 个二进制位:101010
    • 前面补全 2 个零:00 + 101010 = 00101010
    • 00101010 转成十进制:42
    • 根据 base64 对应关系表42 代表 base64 的 q
    • q 的 ASCII 二进制值是:1110001
    • ASCII 的 1110001 补全为 8 bit:01110001(即最终传输的内容)
  • 最终:

    • 输入:原始 6 个二进制位 101010
    • 输出:转换成了 ASCII 的 01110001
      • 也即用户能看到的 ASCII 值:q
    • 从 6 个变为 8 个二进制位,即为原来大小的 8/6 = 4/3 倍
      • 即变大了,有冗余
  • 若输入内容二进制位数量不能刚好被 6 整除,则参照 阮老师的说明理解,这里不详述

为什么每组 6个二进制位 前都加 2 个二进制位 00

答(个人意见 + 部分参考):

  • 首先 6 个二进制位 是为了控制总共 2^6 = 64 种可能
  • 一般计算机处理都是以 8个二进制位(即 1 个这字节)为单位处理的,所以需要前面补全两个零
    • 为什么不是后面补零?
    • 猜:是因为这样不好计算二进制的实际代表的十进制数值(从右起算)
  • 为什么不直接按 8 个二进制位 分组?
    • 因为 ASCII 可打印字符 没那么多,具体参考后面解释

为什么末尾要用 = 来补全?

答:

  • 是为了将原始内容的二进制位数凑齐为 6 的倍数,这样才能被切为整数个 6 个二进制位
  • 为什么是 = 而不是其他字符如: ~!@#$%^&*()
    • 未知

为什么常用 base64 而非 base16/base32/base95/base128/base256?

这里只讨论:为什么网页图片、Email 内容最常用的是 base64 而非其他基数?

不讨论:特殊用途的其他基数的 base 的存在利用,如 base58

答(个人意见 + 部分参考):

编码后得到的字符:

  • 应是可见字符

    • 方便人类查看
  • 应是 ASCII 内的字符

    • (猜)绝大部分计算机、网络设备能处理 ASCII 的字符
    • ASCII 总字符数 128 个(8 bit 空间,即 1 字节),其中 可打印字符 95 个
      • 所以可选择的范围缩小到 95 个
  • 若采用 base16

    • 则范围是 16 个,即 2^4,即 4 个二进制位
    • 则只要输入内容的二进制位是 4 的倍数即可,实际大部分的输入内容二进制位都是 8 的倍数,则肯定是 4 的倍数
    • 问题在于:
      • base16 得到的 4 个二进制位 都需要补全到 8 个二进制位
        • 因为大部分计算机的处理都是以 8 个二进制位 作为处理基本单位
      • 所以 base16 从原始数据变为最终编码,数据变大了:8/4 = 2,为原来数据的两倍大,比 base64 的 4/3 大
  • 若采用 base95(或 65-95 中选一个)

    • 则范围是 ASCII 的 95 个全部可打印字符
    • 问题在于:
      • (猜)有些可打印字符不好看
      • (猜)有些可打印字符与其他用途字符有冲突?
      • 64(2^6) < 95 < 128(2^7)
        • 所以也得才采用 7 个二进制位
        • 在对原始数据的二进制内容进行分组时,只能按照 7 个二进制位 为 1 组切分
        • 这时会有 一个问题:被切分得到的 7 个二进制位 包含 128 种情况,总会有 95 不能表示的情况
        • 所以不能采用不与二进制对齐的 base 基数
        • 即只能考虑这些情况:base16(2^4)、base32(2^5)、base64(2^6)、base128(2^7)、base256(2^8)
    • 其他 base 基数参考:
  • 若采用 base32

    • 则范围是 32 个,即 2^5,即 5 个二进制位
    • 编码后大小变为原来数据的 8/5,也比 base64 的 4/3 大
  • 若采用 base128

  • 若采用 base256

    • 同理于 base128
  • 综上所述:

    • 采用 base64 是当时一个客观的唯一选择
    • 其他情况:
      • 要么没与 2 的次方对齐,导致原始信息不能一一匹配
      • 要么每个分组补全太多,性能不是最优

以下理由应该不是 base64 的原因(之前认为是):

  • 为了与 1 字节(bytes) = 8 bit = 2^8 对齐,对齐的好处:
  • 性能更优(空间换时间)
  • 便于并行处理,前面数据的编码解码不影响后面数据

    若不对齐,前面数据会影响后面数据的编码、解码

  • 若传输出错,便于纠错?

其他

base64 与 UTF-8 无关,base64 只关心输入内容的二进制。

base64 使用场景

  • 网页图片
  • Email 内容