TOC

Base32,Base16

Base64 非常好,可是有些时候我们讨厌引入标点符号 +/=

Base32 每 5bits 转换成一个字符,数据膨胀至 8/5 = 1.6 倍(相比之下,Base64 膨胀至 4/3 = 1.33 倍)。
Base16 就更简单了,就是 16 进制编码(),最大优点是不存在补位的情况,缺点是每个字节转成两个字符,数据量翻一番。

参照前面 Base64 的例子,我们手算一遍 Base32 和 Base16 版本的 hello world 转换。

Base32

根据 RFC4648:

  • Base32 字母表为:A-Z, 2-7
    PS: 0 和 1 因为和字母 O 和 I 容易混淆,所以跳过。
  • 最后长度填充至 8 的倍数
bin       dec  hex  char
--------  ---  ---  ----
01101000  104   68     h
01100101  101   65     e
01101100  108   6c     l
01101100  108   6c     l
01101111  111   6f     o
00100000   32   20
01110111  119   77     w
01101111  111   6f     o
01110010  114   72     r
01101100  108   6c     l
01100100  100   64     d

按五位一个的话就是(PS: 后方须补两个 0: 8 x 11 = 88 = 5 x 18 - 2):

bin    dec  hex  b32
-----  ---  --- ----
01101   13   0d    N
00001    1   01    B
10010   18   12    S
10110   22   16    W
11000   24   18    Y
11011   27   1b    3
00011    3   03    D
01111   15   0f    P
00100    4   04    E
00001    1   01    B
11011   27   1b    3
10110   22   16    W
11110   30   1e    6
11100   28   1c    4
10011   19   13    T
01100   12   0c    M
01100   12   0c    M
10000   16   10    Q

得到 ceil(11 x 1.6) = 18 个字符,最后填充至 24 个字符,也就是加 6 个等于号:

NBSWY3DPEB3W64TMMQ======

Base32 验证

echo -n 'hello world' | base32
NBSWY3DPEB3W64TMMQ======

Base16

bin   dec  hex  b16
----  ---  --- ----
0110    6   06    6
1000    8   08    8
0110    6   06    6
0101    5   05    5
0110    6   06    6
1100   12   0c    C
0110    6   06    6
1100   12   0c    C
0110    6   06    6
1111   15   0f    F
0010    2   02    2
0000    0   00    0
0111    7   07    7
0111    7   07    7
0110    6   06    6
1111   15   0f    F
0111    7   07    7
0010    2   02    2
0110    6   06    6
1100   12   0c    C
0110    6   06    6
0100    4   04    4

68656C6C6F20776F726C64

Base16 验证

echo -n 'hello world' | base16 -c
68656C6C6F20776F726C64

填充规则

直到研究 Base32 才发现我对 Base64 填充违规有一点误解。

Base64 6bits 一组,可能会有以下三种情况:

  • 3n => 4n
  • 3n + 1 => 4n + 1 + 2, 补 4 个 0,加两个等于号
  • 3n + 2 => 4n + 3 + 1, 补 2 个 0,加一个等于号

我理解的填充规则是为了标记补 0 数,等于号数量乘以二。解析的时候计算等于号的数量,然后将前面的字符转换成字节,最后按照等于号数量乘以二去掉后缀 0。

没想到 RFC 规范中说的是对齐到 4 的倍数。
这两种理解在 Base64 中是一个效果,但是 Base32 中不一样。

Base32 5bits 一组,会有以下几种情况:

  1. 5n => 8n
  2. 5n + 1: 8n + 1, 多了 3bits (8 - 5),需补 2 个 0
  3. 5n + 2: 8n + 3, 还多了 1bit (8 x 2 - 5 x 3),需补 4 个 0
  4. 5n + 3: 8n + 4, 还多了 4bits (8 x 3 - 5 x 4),需补 1 个 0
  5. 5n + 4: 8n + 6, 还多了 2bits (8 x 4 - 5 x 6),需补 3 个 0

如果按照我的理解,那就分别加上 0,2, 4, 1, 3 个等于号,最终分别是 8n, 8n + 2, 8n + 4, 8n + 5, 8n + 7 个字符。

但是规范是要求对齐到 8 的倍数,那就是分别需要加上 0, 6, 4, 3, 2 个等于号,除了第一个,下面四种情况填充等于号之后都是 8(n+1) 个字符。

助记:字节数,除五取余,补零 2413,填充 6432。

其他的 Base32 方案

z-base-32

字母表: ybndrfg8ejkmcpqxot1uwisza345h769

crockford's base32

字母表: 0123456789ABCDEFGHJKMNPQRSTVWXYZ

  1. 大小写不敏感
  2. 拿掉四个字母: ILO
  3. 如果出现字母 o 当做 0 处理
  4. 如果出现字母 i, l, 当做 1 处理

手写友好。

base32hex

相当于沿着十六进制编码的思路拓展。

字母表: 0123456789ABCDEFGHIJKLMNOPQRSTUV

geohash

字母表: 0123456789bcdefghjkmnpqrstuvwxyz

拿掉四个字母: ailo

video games (nintendo)

字母表: 0123456789bcdfghjklmnpqrstvwxyz

拿掉 AEIOU 五个元音字母,补充一个标点符号,避免出现脏话。

word-safe base32

word-safe base20: 23456789CFGHJMPQRVWX, 8digits+12uppers
word-safe base32: 23456789CFGHJMPQRVWXcfghjmpqrvwx, 8digits+12uppers+12lowers

设计目标是避免形成单词。