Base64编解码原理及TS语言中的实现(以你容易理解的方式:未使用让人头大的位运算)

一、Base64原理

Base64码表:

索引对应字符索引对应字符索引对应字符索引对应字符
0A17R34i51z
1B18S35j520
2C19T36k531
3D20U37l542
4E21V38m553
5F22W39n564
6G23X40o575
7H24Y41p586
8I25Z42q597
9J26a43r608
10K27b44s619
11L28c45t62+
12M29d46u63/
13N30e47v
14O31f48w
15P32g49x
16Q33h50y

原理:

1.原数据每个字节均用8个完整的二进制位表示,不足时前补0

原数据二进制位总长恰为8与6的公倍数时(如下图),以6个二进制位一组进行重新分割

恰好分割完,最后一组无需补0,转出的base64编码字码串也无需加"="

原数据Mz1
原数据(十六进制)0x4D0x7A0x31
原数据(二进制)010011010111101000110001
重新分割010011010111101000110001
对应数字(也即码表中的索引)19234049
对应base64码TXox

Mz1 编码结果 TXox

2.原数据每个字节均用8个完整的二进制位表示,不足时前补0

原数据二进制位总长不是8与6的公倍数时(如下图),以6个二进制位一组进行重新分割

最后一组需用0补足6位,得到的Base64字符串长度必须为4的倍数,不足时用"="补足

原数据Mz
原数据(十六进制)0x4D0x7A
原数据(二进制)0100110101111010
重新分割010011010111101000
对应数字(也即码表中的索引)192340最终的base64码串长不是4的整数倍时以"="补足
对应base64码TXo=

Mz 编码结果 TXo=

M   编码结果 TQ==

二、Base64编解码在TypeScript中的实现

/**
 * Base64编解码工具
 * (原数据可以是字符字节数组也可以是任何格式的文件转换的字节数组)
 * 字节数组与base64字符串互转
 */
export class Base64{
  /**
   * base64的码表
   */
  private static readonly base64Codes:string= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"

  /**
   * 将字节数组转为base64字符串
   * @param bytes 原数据
   * @returns base64字符串
   */
  static encode(bytes:number[]):string{
    let result=''
    for (let num of bytes){
      let numStr:string=num.toString(2)//数转成2进制串
      result+=Base64.preFill(numStr)//将每个二进制串从头部补齐到8位
    }
    //将二进制从尾部补到6的整数倍
    let len=result.length
    let endFillLen=(len%6!=0)?(6-len%6):0
    result=Base64.endFill(result,len+endFillLen)
    let newBytes:number[]=[]
    for (let i = 0; i < result.length; i=i+6) {
      const s =result.slice(i,i+6)//分割出二进制字符串形如01000110
      let n=parseInt(s,2)//将s作为二进制串转成数字
      newBytes.push(n)
    }
    result=''
    for (let byte of newBytes){
      //从base64编表中取对应字符
      let str=Base64.base64Codes.slice(byte,byte+1)
      result+=str
    }
    let len1=result.length
    let endFillLen1:number=(len1%4==0)?0:(4-len1%4)
    result=Base64.endFill(result,len1+endFillLen1,'=',4)//如果长度不是4的倍数用"="补足
    return result
  }
  static decode(base64Str:string):number[]{
    let reg:string="^([A-Za-z0-9+/]{4})+$" +
    "|^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{3}=)$" +
    "|^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{2}={2})$" +
    "|^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{1}={3})$";
    if(!base64Str.match(reg))return []//验证base64Str是否非法
    //如果末尾有"="全部去掉
    base64Str=base64Str.replace('=','')
    //将每个字符转成number,即byte
    let bit6Str:string=''//每组6位的二进制字符串
    for (let i = 0; i < base64Str.length; i++) {
      const c= base64Str[i];
      const byte = Base64.base64Codes.indexOf(c)
      bit6Str+=Base64.preFill(byte.toString(2),6)
    }
    //转成每组8位的二进制字符串
    let bit6StrLen:number=bit6Str.length
    let deleteLen:number=bit6StrLen%8
    let bit8Str=bit6Str.slice(0,bit6StrLen-deleteLen)
    //将bit8Str按每8位一组放入数组
    let bytes:number[]=[]
    for (let i = 0; i < bit8Str.length; i=i+8) {
      //将每8位bit8Str作为二进制字符串转成数
      let num:number=parseInt(bit8Str.slice(i,i+8),2)
      bytes.push(num)
    }
    return bytes
  }
  /**
   * 不足8位时的前补0函数
   * @param str 原字符串长度
   * @param toLength 将要达到的总长
   * @param fillChar 用以填充的字符
   * @param groupLength 每组的标准长度(比如二进制8位)
   * @returns 补齐到toLength长度后的字符串
   */
  private static preFill(str:string,toLength:number=8,fillChar:string='0',groupLength:number=8):string{
    if(str.length%groupLength==0)return str
    let len=str.length
    //需要补的位数
    let preFillLen=toLength-len
    for (let i = 0; i < preFillLen; i++) {
      str=fillChar+str
    }
    return str
  }
  /**
   * 不足6位时的后补0函数
   * @param str 原字符串长度
   * @param toLength 将要达到的总长
   * @param fillChar 用以填充的字符
   * @param groupLength 每组的标准长度(比如base64每组截取6个二进制位)
   * @returns  补齐到toLength长度后的字符串
   */
  private static endFill(str:string,toLength:number=6,fillChar:string='0',groupLength:number=6):string{
    //console.log(str.length +':'+ groupLength+);
    if(str.length%groupLength==0)return str//如果已齐就不再补
    let len=str.length
    //需要补的位数
    let endFillLen=toLength-len
    for (let i = 0; i < endFillLen; i++) {
      str=str+fillChar
    }
    return str
  }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值