Base64在我看来很重要的一个用途是将复杂的码比如GBK、UTF8、MIME等复杂的码,变成单字节的简单ASCII字符,便于在各种不同环境(计算机)之间传递信息。这很好理解,只要是计算机,必须得支持ASCII标准,但不一定支持其它编码。

Base64的编码过程是将每三个字节即24个bit,变成以6个bit为一组的组集,共有24/6=4组。每个组的6个bit最高能表示2^6即64个数,这也是Base64的由来。这64个数的表示区间为[0,63],建立一个字符索引表,输入值为[0,63],输出表中对应字符。不同的Base64变种在编码过程主要是索引表不一样。(可能你有个疑问,每轮需要3个byte,这必须使得需要编码的byte长度整除3,不整除3怎么办?后面后讲到)。

Base64编码详解及其变种(解决加号在URL变空格问题)

如图所示,3个byte用红、紫、绿表示,4个组A=A1 A2 A3 A4 A5 A6,B= B1 B2 B3 B4 B5 B6,C= C1 C2 C3 C4 C5 C6,D = D1 D2 D3 D4 D5 D6。因为在绝大多数语言中,byte都为最小操作单元,所以这四个组的输出byte值将会是A’ = 0 0 A1 A2 A3 A4 A5 A6,B’ = 0 0 B1 B2 B3 B4 B5 B6 , C’= 0 0 C1 C2 C3 C4 C5 C6,D’ = 0 0 D1 D2 D3 D4 D5 D6。一个byte只需要用到其中的6个bit,当然最高两位要置0了。

应用一点点计算机编码知识,假设红byte为R,紫byte为P,绿byte为G,那么:

  • A’ = R >> 2,红byte右移两位,表示A取R的高六位。
  • B’ = (R << 4 & 0x3F) | P >> 4。R左移4位变为A5 A6 B1 B2 0 0 0 0,看B’的红色部分高两位为零,所以要 & 上0x3f,因为0x3f的二进制表示为0 0 1 1 1 1 1 1,这样就变成 0 0 B1 B2 0 0 0 0 。P >> 4将P的高四位变为低四位,高四位置0,变为0 0 0 0 B3 B4 B5 B6。很显然 0 0 B1 B2 0 0 0 0  | 0 0 0 0 B3 B4 B5 B6 = 0 0 B1 B2 B3 B4 B5 B6
  • C’ = (P << 2 & 0x3F) | G >> 6。P左移2位变为B5 B6 C1 C2 C3 C3 C4 0 0,看C’的红色部分高两位为零,所以要 & 上0x3f,这样值为0 0 C1 C2 C3 C3 C4 0 0。G右移6位为 0 0 0 0 0 0 C5 C6。很显然 0 0 C1 C2 C3 C3 C4 0 0 | 0 0 0 0 0 0 C5 C6 = 0 0 C1 C2 C3 C4 C5 C6。
  • D’ = G & 0x3F。只需将G的高两位C5,C6置0,就是0 0 D1 D2 D3 D4 D5 D6。

不能整除3怎么办?

上面讲的是byte长度能整除3,实际只有1/3概率可整除,另两个1/3是余数为1和余数为2。对于不整除的情况,Base64的做法是补齐,不是补齐byte,而是补齐编码之后的子串,使编码字串能够被4整除,因为解码只能是4个字符解成3个byte。补齐字串用了第65个字符 = 即等号。下面分别描述。

余数为1的情况

余数为1也就是上图只能剩红byte R了,那么:

  • A’ = R >> 2。这个保持不变。
  • B’ = R << 4 & 0x3F。P没有了,只能取R的最低两位了。
  • C’ = '=',即C’为填充字符= 。
  • D’ = '=',即D’为填充字符= 。

余数为2的情况

余数为2也就是上图有红byte R,紫byte P,绿byte G没有了。那么:

  • A’ = R >> 2。这个保持不变。
  • B’ = (R << 4 & 0x3F) | P >> 4。这个也保持不变。
  • C’ = P << 2 & 0x3F。因为G没有了,只能取P的低四位。
  • D’ = '=',即D’为填充字符= 。

前面讲到,不同Base64编码只是字符索引表不一样,最正宗的Base64使用了如下字符索引表。

/* 索引 0 ~ 5*/
/* 索引6 ~ 18*/
/* 索引 19 ~ 31*/
/* 索引 32 ~ 44*/
/* 索引 45 ~ 57*/
/* 索引58 ~ 63*/

相关文章: