理解Base64和Base32

less than 1 minute read

Base64和Base32是两种常见的编码方式。Base64早已接触过,比如在Basic Auth中对用户名和密码做编码,对图像做编码后将图像内容放在HTTP请求体中(在yitu工作时见过)。而Base32则是最近看ULID资料时看到的。

Base64是基于64个可打印字符来表示二进制数据的方法。每个字符表示6个bit(2^6=64)。3个字节即24个bit,对应着4个Base64字符,所以3个字节可由4个可打印字符来表示。

Base64编码中,数值和字符的映射关系见下表:

其中数值0-25映射为A-Z,26-51映射为a-z,52-61映射为0-9,+和/映射为62和63。

举个例子,单词”Hey”的Base64编码为”SGV5”:

$ echo -n "Hey" | base64
SGV5

这是怎么得出来的呢?

H,e,y三个字母的ASCII编码(十进制表示)是72,101,121,转换成二进制是01001000,01100101,01111001。把这24个bit切成4个Base64单元就是:010010,000110,010101,111001,转换成十进制:18,6,21,57,到上面的映射表中查找可得:S,G,V,5。

解码就是上述过程逆向行之即可。

$ echo -n SGV5 | base64 -d
Hey

再看一个字节数不能被3整除的例子:对空格字符做编码。

$ echo -n " " | base64
IA==

空格的ASCII编码是32,即00100000。先将其补足到3个字节即24个bit长度:00100000,00000000,00000000。切成4个Base64单元:001000,000000,000000,000000,转换成十进制:8,0,0,0。8映射为I,0映射为A,最后两个用=表示。

Base32则是基于32个字符来表示二进制数据,每个字符表示5个bit(2^5=32)。相比于Base64的优点是,不区分大小写,这样打字时不易出错,并且便于人类语音交流;但是紧凑性(compactness)不如Base64。

Base32有多种变体,这里看一下 Crockford’s Base32。这种编码方式中的32个字符由10个数字和26个字母中除去I,L,O,U组成。因为I,L容易和数字1混淆,O容易和数字0混淆,U则是”Accidental obscenity”。

解码时,大小写字母都接受,i和L被视为1处理,o被视为0处理;编码时,只使用大写字母。

如果原二进制数据的位数不是5的倍数,则要用0补齐为5的倍数后再编码。

以单词”The”举例,三个字母的ASCII编码是84,104,101,转换为二进制为:01010100,01101000,01100101,总共24bit。用1个0补齐到25bit后,再切分为5个Base32单元:01010,10001,10100,00110,01010,转换为十进制:10,17,20,6,10,查编码表后映射为:A,H,M,6,A。

我们可以通过一个例子直观感受下这两种编码方式带来的“字符数膨胀“。 原文本:”The quick brown fox jumps over the lazy dog.”,总共44个字符。 Base64编码(RFC 1421):”VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZy4=”,总共60个字符。 Crockford’s Base32编码:”AHM6A83HENMP6TS0C9S6YXVE41K6YY10D9TPTW3K41QQCSBJ41T6GS90DHGQMY90CHQPEBG”,总共71个字符。

参考资料:

Comments