Base64原理及实现

base64
2019年02月19日

base64

Base64是用64个字符来表示数据的方法。Base64要求把每三个8Bit的字节转换为四个6Bit的字节,然后把6Bit再添两位高位0,组成四个8Bit的字节,也就是说,转换后的字符串理论上将要比原来的长1/3。如果要转码数据字节不是3的倍数,最后一组填充1到2个0字节,并在最后编码完成后在结尾添加1到2个=号。 转换后,我们每个字节范围为[00000000-00111111],所以我们可以用一个64码表来得到我们想要的字符串(也就是最终的Base64编码)标准的Base64用[A-Z,a-z,0-9,+,/]64个字符编码。

编码

深入了解base64前,我们需要先了解编码。编码是信息从一种形式或格式转换为另一种形式的过程。计算机中,所有的数据在存储和运算时都使用二进制示,n位二进制组合成2的n次方个不同的信息,给每个信息规定一个具体码组,这种过程叫编码。而具体用哪些二进制数字表示哪个符号,每个人都可以约定自己的一套编码。

如常用的ASCII是由美国国家标准学会制定的一种单字节字符编码,标准ASCII码使用7位二进制数(剩下的1位二进制为0)来表示所有的大写和小写字母,数字0到9、标点符号。在有的语言中128个符号是不够的,有些编码方案利用字节中闲置的最高位编入新的符号,有的利用两个(GBK等)或更多字节(utf-32等)表示一个符号(汉字)

Base64编码

base64编码过程:

  • ascii码s133对应的编码115 49 51 51
  • 在内存2进制表示: 01110011 00110001 00110011 00110011
  • 每三组分为6Bit四组011100 110011 000100 110011 001100 110000 000000 000000
  • 高位补0得到 00011100 00110011 00000100 00110011 00001100 00110000 00000000 00000000
  • 对应十进制 28 51 4 51 12 48 0 0
  • 查对照表 生成base64码czEzMw==

base64解码过程:

base64编码过程逆向即为解码

javascript代码实现

Base64 = {
 _table: [
 'A',
 'B',
 'C',
 'D',
 'E',
 'F',
 'G',
 'H',
 'I',
 'J',
 'K',
 'L',
 'M',
 'N',
 'O',
 'P',
 'Q',
 'R',
 'S',
 'T',
 'U',
 'V',
 'W',
 'X',
 'Y',
 'Z',
 'a',
 'b',
 'c',
 'd',
 'e',
 'f',
 'g',
 'h',
 'i',
 'j',
 'k',
 'l',
 'm',
 'n',
 'o',
 'p',
 'q',
 'r',
 's',
 't',
 'u',
 'v',
 'w',
 'x',
 'y',
 'z',
 '0',
 '1',
 '2',
 '3',
 '4',
 '5',
 '6',
 '7',
 '8',
 '9',
 '+',
 '/'
 ],
 _getReg() {
 return new RegExp(`^[${this._table.join('')}]+={0,2}$`);
 },
 encode(bin) {
 const codes = [];
 const binLength = bin.length;
 un = binLength % 3;

 for (let i = 2; i < binLength; i += 3) {
 let c = bin[i - 2] << 16;
 c |= bin[i - 1] << 8;
 c |= bin[i];

 // 0x3f代表16进制数3F即0011 1111
 codes.push(this._table[c >> 18 & 0x3F]);
 codes.push(this._table[c >> 12 & 0x3F]);
 codes.push(this._table[c >> 6 & 0x3F]);
 codes.push(this._table[c & 0x3F]);
 }

 if (un == 1) {
 const c = bin[binLength - 1] << 16;
 codes.push(this._table[c >> 18 & 0x3F]);
 codes.push(this._table[c >> 12 & 0x3F]);
 codes.push('=');
 codes.push('=');
 }
 if (un == 2) {
 let c = bin[binLength - 2] << 16;
 c |= bin[binLength - 1] << 8;
 codes.push(this._table[c >> 18 & 0x3F]);
 codes.push(this._table[c >> 12 & 0x3F]);
 codes.push(this._table[c >> 6 & 0x3F]);
 codes.push('=');
 }

 return codes.join('');
 },
 decode(base64Str) {
 const bin = [];
 const base64StrLen = base64Str.length;

 if (!this._getReg().test(base64Str)) {
 throw 'Base64编码格式错误';
 }
 if (base64StrLen % 4 !== 0) {
 throw 'Base64编码长度错误';
 }

 const eqCount = base64StrLen - base64Str.indexOf('=');

 for (let i = 3; i < base64StrLen; i += 4) {
 let code = 0;

 const [c1, c2, c3, c4] = [base64Str.charAt(i - 3), base64Str.charAt(i - 2), base64Str.charAt(i - 1), base64Str.charAt(i)];

 code = code << 6 | this._table.indexOf(c1);
 code = code << 6 | this._table.indexOf(c2);

 if (c3 !== '=' && c4 !== '=') {
 code = code << 6 | this._table.indexOf(c3);
 code = code << 6 | this._table.indexOf(c4);
 }
 if (c3 !== '=' && c4 === '=') {
 code = code << 6 | this._table.indexOf(c3);
 code = code << 6 | 0;
 }
 if (c3 === '=') {
 code = code << 6 | 0;
 code = code << 6 | 0;
 }

 // 0xff即11111111
 bin.push(code >> 16);
 // 取得低八位
 bin.push(code >> 8 & 0xFF);
 bin.push(code & 0xFF);
 }

 switch (eqCount) {
 case 1:
 bin.pop();
 break;
 case 2:
 bin.pop();
 bin.pop();
 break;
 default:
 break;
 }

 return bin;
 }
};

const str = `1!@#$%^ghghna三个卡就能`;
const ba = Base64.encode(Buffer.from(str));

console.log(`编码前:${str}`); // 1!@#$%^ghghna三个卡就能
console.log(`编码后:${ba}`); // MSFAIyQlXmdoZ2huYeS4ieS4quWNoeWwseiDvQ==
console.log(`解码:${Buffer.from(Base64.decode(ba)).toString()}`); // 1!@#$%^ghghna三个卡就能
Edit this page on GitHub

AltStyle によって変換されたページ (->オリジナル) /