Follow me on GitHub

Base64 简介

维基百科对 Base64 定义如下:

Base64 is a group of similar binary-to-text encoding schemes that represent binary data in an ASCII string format by translating it into a radix-64 representation.

即 Base64 是一组编码、解码方案,用于在二进制数据(binary data)和文本之间互相转换,说是“一组”是因为 Base64 根据编码集不同可以有很多变种。

问题来了,用于二进制和字符串之间转换的编码方案有很多,比如 ASCII、UTF-8 等等,Base64 和他们有什么区别?

区别在于 ASCII 等编码方案的字符集中有 不可见字符,而 Base64 字符集全部是可见字符。

不可见字符会导致:

  • 网络传输过程中,不同网络硬件、软件对字符的要求不同,有的将一些不可变字符用作自己的 控制字符,给予它们特殊含义(如 URL 不允许出现 =(除 query string)和空格),直接将 ASCII 字符转换为对应的二进制传输时,可能导致潜在错误;
  • 用文本编辑器打开二进制文件时,如果用 ASCII 解码,解码结果可能含有很多不可见字符,导致无法编辑;
  • 早期的邮件传输服务仅支持可见字符,无法传输不可见字符(如图片、音频);

Base64 的字符集全部是 可见字符,从而避免以上问题。

为啥叫 Base64 呢,原因是该方案基于 64 个字符,即大小写拉丁字母各 26 个、数字 10 个、加号 + 和斜杠 /,共 64 个字符(等号 = 用来作为占位后缀,因此如果有串无意义的字符,其最后有一个或多个 = 号,则可推测这是 Base64 编码后的字符串),这 64 个字符是常见编码方案的子集,如 ASCII、UTF-8 等,所以使用 Base64 编码后,传输目的地一般能解码。

Base64 编码规则比较简单,首先是一个包含 64 个字符的字符集,字符索引从 0 开始:

1
['A', 'B', 'C', ... 'a', 'b', 'c', ... '0', '1', ... '+', '/']
  • Base64、ASCII 等与十进制、十六进制等基本类似,都是对二进制串的编码,十六进制用 [0, 9] 和 [A, F] 编码,Base64 的编码集更大一些(64),而 ASCII 编码集更大一点(128);
  • 编码集越小,则编码后的长度越长,编码集越大,则编码后长度越短。

然后,将需要编码的 binary data 按每组 6 bits 分组,并把分组后的每组 binary data 转为 10 进制数字:

1
2
0 1 0 0 1 1   |   0 1 0 1 1 0   |   0 0 0 1 0 1   |   1 0 1 1 1 0
19 22 5 46

最后,以 19 等作为索引,在字符集中查找对应的字符:

1
2
3
0 1 0 0 1 1   |   0 1 0 1 1 0   |   0 0 0 1 0 1   |   1 0 1 1 1 0
19 22 5 46
T W F u

所以二进制数据 0 1 0 0 1 1 0 1 0 1 1 0 0 0 0 1 0 1 1 0 1 1 1 0 经 Base64 编码后结果为 TWFu

上面的二进制数字串是字符串 Man 的二进制表示,因此,使用 Base64 编码字符串时,实际会经过两次编码:

1
字符串 -> binary data -> 字符串

其中,Base64 出现在第二次编码时。

注意,第一次转换时,每个字符转换为 8 bits,第二次转换时,每 6 bits 转换为一个字符,因此最终的结果字符串长度要比原始串大 1/3 左右。

考虑网络传输场景,若不使用 Base64,字符串需要 2 次编码、解码:

1
字符串 -> binary data(网络传输) -> 字符串

若使用 Base64,需要经过 4 次编码、解码:

1
字符串 -> binary data -> Base64 字符串 -> binary data(网络传输) -> Base64 字符串 -> binary data -> 字符串

最后,通过修改 Base64 的字符集,可以衍生出很多变种,例如前面说过,URL 中不能出现 /,且除 query string 部分外,不能出现 =,Base64 又恰好用到 /=,因此一种变种为:

  • + 替换为 -
  • / 替换为 _
  • 去掉用于 padding 的 =

这样就能用于编码 URL 了。


参考: