- base64
- base64 允许的字符
- base64 编码规则
- 为什么每组
6个二进制位
前都加 2 个二进制位00
? - 为什么末尾要用
=
来补全? - 为什么常用 base64 而非 base16/base32/base95/base128/base256?
- 其他
- base64 使用场景
原始字符串:
Hello World!
base64 编码后:
SGVsbG8gV29ybGQh
比例:
转换后
/ 转换前
= 16 / 12 = 4 / 3
假设转换前是 ASCII 编码
base64 只允许出现以下字符,所有原始内容都要通过一定规则转换为下列字符:
a-z
A-Z
0-9
- 2 个特殊字符:
+
、/
- 1 个末尾补全字符:
=
数量:26 个英文字母 * 2 + 2 个特殊字符 = 64(即 base64 中的 64
)
若算上补全字符 =
应该是 65 个允许的字符
先明确 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 个二进制位:
-
最终:
- 输入:原始 6 个二进制位
101010
- 输出:转换成了 ASCII 的
01110001
- 也即用户能看到的 ASCII 值:
q
- 也即用户能看到的 ASCII 值:
- 从 6 个变为 8 个二进制位,即为原来大小的 8/6 = 4/3 倍
- 即变大了,有冗余
- 输入:原始 6 个二进制位
-
若输入内容二进制位数量不能刚好被 6 整除,则参照 阮老师的说明理解,这里不详述
答(个人意见 + 部分参考):
- 首先
6 个二进制位
是为了控制总共 2^6 = 64 种可能 - 一般计算机处理都是以
8个二进制位(即 1 个这字节)
为单位处理的,所以需要前面补全两个零- 为什么不是后面补零?
- 猜:是因为这样不好计算二进制的实际代表的十进制数值(从右起算)
- 为什么不直接按
8 个二进制位
分组?- 因为 ASCII 可打印字符 没那么多,具体参考后面解释
答:
- 是为了将原始内容的二进制位数凑齐为 6 的倍数,这样才能被切为整数个
6 个二进制位
- 为什么是
=
而不是其他字符如:~!@#$%^&*()
?- 未知
这里只讨论:为什么网页图片、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 大
- base16 得到的
- 则范围是 16 个,即 2^4,即
-
若采用 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 大
- 则范围是 32 个,即 2^5,即
-
若采用 base128
- 则范围是 128 个,即 2^6,即
7 个二进制位
- ASCII 没那么多可打印字符,所以不考虑
- 参考:https://stackoverflow.com/a/6008744/2752670
- 则范围是 128 个,即 2^6,即
-
若采用 base256
- 同理于 base128
-
综上所述:
- 采用 base64 是当时一个客观的唯一选择
- 其他情况:
- 要么没与 2 的次方对齐,导致原始信息不能一一匹配
- 要么每个分组补全太多,性能不是最优
以下理由应该不是 base64 的原因(之前认为是):
- 为了与 1 字节(bytes) = 8 bit = 2^8 对齐,对齐的好处:
- 性能更优(空间换时间)
- 便于并行处理,前面数据的编码解码不影响后面数据
若不对齐,前面数据会影响后面数据的编码、解码
- 若传输出错,便于纠错?
base64 与 UTF-8 无关,base64 只关心输入内容的二进制。
- 网页图片
- Email 内容
- 等