为了账号安全,请及时绑定邮箱和手机立即绑定

【备战春招】第9天 破解JavaScript高级玩法 第十六讲

标签:
JavaScript

课程章节: 绕不过坎,字符编码

主讲老师: Cloud

课程内容:

今天学习的内容包括:

各类字符编码

课程收获:

16.1 心得:

utf-8转base64 编码规则实现

function to_binary(str) {
  const string = str.replace(/\r\n/g, "\n");
  let result = "";
  let code;
  for (var n = 0; n < string.length; n++) {
    //获取码点
    code = str.charCodeAt(n);
    if (code < 0x007f) {
      // 1个字节
      // 0000 0000 ~ 0000 007F  0 ~ 127 1个字节

      // (code | 0b100000000).toString(2).slice(1)
      result += code.toString(2).padStart(8, "0");
    } else if (code > 0x0080 && code < 0x07ff) {
      // 0000 0080 ~ 0000 07FF	128 ~ 2047 2个字节
      // 0x0080 的二进制为 10000000 ,8位,所以大于0x0080的,至少有8位
      // 格式 110xxxxx 10xxxxxx

      // 高位 110xxxxx
      result += ((code >> 6) | 0b11000000).toString(2);
      // 低位 10xxxxxx
      result += ((code & 0b111111) | 0b10000000).toString(2);
    } else if (code > 0x0800 && code < 0xffff) {
      // 0000 0800 ~ 0000 FFFF	2048 ~ 65535	3个字节
      // 0x0800的二进制为 1000 00000000,12位,所以大于0x0800的,至少有12位
      // 格式 1110xxxx 10xxxxxx 10xxxxxx

      // 最高位 1110xxxx
      result += ((code >> 12) | 0b11100000).toString(2);
      // 第二位 10xxxxxx
      result += (((code >> 6) & 0b111111) | 0b10000000).toString(2);
      // 第三位 10xxxxxx
      result += ((code & 0b111111) | 0b10000000).toString(2);
    } else {
      // 0001 0000 ~ 0010 FFFF   65536 ~ 1114111   4个字节
      // https://www.unicode.org/charts/PDF/Unicode-13.0/U130-2F800.pdf
      throw new TypeError("暂不支持码点大于65535的字符");
    }
  }
  return result;
}



const BASE64_CHARTS =
  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
function utf8_to_base64(str) {
  let binaryStr = to_binary(str);
  const len = binaryStr.length;

  // 需要填补的=的数量
  let paddingCharLen = len % 24 !== 0 ? (24 - (len % 24)) / 8 : 0;

  //6个一组
  const groups = [];
  for (let i = 0; i < binaryStr.length; i += 6) {
    let g = binaryStr.slice(i, i + 6);
    if (g.length < 6) {
      g = g.padEnd(6, "0");
    }
    groups.push(g);
  }

  // 求值
  let base64Str = groups.reduce((b64str, cur) => {
    b64str += BASE64_CHARTS[+`0b${cur}`];
    return b64str;
  }, "");

  // 填充=
  if (paddingCharLen > 0) {
    base64Str += paddingCharLen > 1 ? "==" : "=";
  }

  return base64Str;
}


console.log(utf8_to_base64("a")); // YQ==

console.log(utf8_to_base64("Ȃ"));  // yII=

console.log(utf8_to_base64("中国人")); // 5Lit5Zu95Lq6

console.log(utf8_to_base64("Coding Writing 好文召集令|后端、大前端双赛道投稿,2万元奖池等你挑战!"));
//Q29kaW5nIFdyaXRpbmcg5aW95paH5Y+s6ZuG5Luk772c5ZCO56uv44CB5aSn5YmN56uv5Y+M6LWb6YGT5oqV56i/77yMMuS4h+WFg+WlluaxoOetieS9oOaMkeaImO+8gQ==

抽象把字符转为utf-8格式二进制的方法

function to_binary(str) {
  const string = str.replace(/\r\n/g, "\n");
  let result = "";
  let code;
  for (var n = 0; n < string.length; n++) {
    //获取码点
    code = str.charCodeAt(n);
    if (code <= 0x007f) {
      // 1个字节
      // 0000 0000 ~ 0000 007F  0 ~ 127 1个字节

      // (code | 0b100000000).toString(2).slice(1)
      result += code.toString(2).padStart(8, "0");
    } else if (code > 0x0080 && code <= 0x07ff) {
      // 0000 0080 ~ 0000 07FF	128 ~ 2047 2个字节
      // 0x0080 的二进制为 10000000 ,8位,所以大于0x0080的,至少有8位
      // 格式 110xxxxx 10xxxxxx

      // 高位 110xxxxx
      result += ((code >> 6) | 0b11000000).toString(2);
      // 低位 10xxxxxx
      result += ((code & 0b111111) | 0b10000000).toString(2);
    } else if (code > 0x0800 && code <= 0xffff) {
      // 0000 0800 ~ 0000 FFFF	2048 ~ 65535	3个字节
      // 0x0800的二进制为 1000 00000000,12位,所以大于0x0800的,至少有12位
      // 格式 1110xxxx 10xxxxxx 10xxxxxx

      // 最高位 1110xxxx
      result += ((code >> 12) | 0b11100000).toString(2);
      // 第二位 10xxxxxx
      result += (((code >> 6) & 0b111111) | 0b10000000).toString(2);
      // 第三位 10xxxxxx
      result += ((code & 0b111111) | 0b10000000).toString(2);
    } else {
      // 0001 0000 ~ 0010 FFFF   65536 ~ 1114111   4个字节
      // https://www.unicode.org/charts/PDF/Unicode-13.0/U130-2F800.pdf
      throw new TypeError("暂不支持码点大于65535的字符");
    }
  }
  return result;
}

// 01100001
console.log(to_binary('a'))
// 11100110 10001110 10011000
// 11100110 10001110 10011000
console.log(to_binary('掘'))

encodeURI

// 获取字符串 utf-8 编码后的二进制串
// 比如 人(三个字节) => 11100100 10111010 10111010
function to_binary(str) {
    const string = str.replace(/\r\n/g, "\n");
    let result = "";
    let code;
    for (var n = 0; n < string.length; n++) {
        //获取麻点
        code = str.charCodeAt(n);
        if (code < 0x007F) { // 1个字节
            // 0000 0000 ~ 0000 007F  0 ~ 127 1个字节

            // (code | 0b100000000).toString(2).slice(1)
            result += (code).toString(2).padStart(8, '0');
        } else if ((code > 0x0080) && (code < 0x07FF)) {
            // 0000 0080 ~ 0000 07FF	128 ~ 2047 2个字节
            // 0x0080 的二进制为 10000000 ,8位,所以大于0x0080的,至少有8位
            // 格式 110xxxxx 10xxxxxx     

            // 高位 110xxxxx
            result += ((code >> 6) | 0b11000000).toString(2);
            // 低位 10xxxxxx
            result += ((code & 0b111111) | 0b10000000).toString(2);
        } else if (code > 0x0800 && code < 0xFFFF) {
            // 0000 0800 ~ 0000 FFFF	2048 ~ 65535	3个字节
            // 0x0800的二进制为 1000 00000000,12位,所以大于0x0800的,至少有12位
            // 格式 1110xxxx 10xxxxxx 10xxxxxx

            // 最高位 1110xxxx
            result += ((code >> 12) | 0b11100000).toString(2);
            // 第二位 10xxxxxx
            result += (((code >> 6) & 0b111111) | 0b10000000).toString(2);
            // 第三位 10xxxxxx
            result += ((code & 0b111111) | 0b10000000).toString(2);
        } else {
            // 0001 0000 ~ 0010 FFFF   65536 ~ 1114111   4个字节 
            // https://www.unicode.org/charts/PDF/Unicode-13.0/U130-2F800.pdf
            throw new TypeError("暂不支持码点大于65535的字符")
        }
    }
    return result;
}

// utf-8的编码后的二进制串 转为 %XX的格式
// 类似encodeURI/encodeURIComponent, 但是未处理不需要编码的字符
function myEncodeURI(str) {
    // 获取utf-8编码后的二进制
    const bites = to_binary(str);
    // 计算字节数
    const byteCount = bites.length / 8;

    // 8个分组, 为一个字节
    // ['00111111','11111100' ]
    const groups = Array.from({
        length: byteCount
    }, (val, index) => {
        return bites.substring(index * 8, index * 8 + 8)
    })
    console.log('byte groups:', groups.join(' '))

    // 0b11111111
    // eval(0b11111111) => 十进制
    // eval(0b11111111).toString(16) => 16进制
    const codes = groups.map(v => eval(`0b${v}`).toString(16).toUpperCase());
    return '%' + codes.join('%')
}

const testArr = [' ', '人'];

console.log("str:", testArr[0])
console.log('codes:'.padEnd(10, ' '), myEncodeURI(testArr[0]));
console.log('encodeURI:', encodeURI(testArr[0]))

console.log();
console.log("str:", testArr[1])
console.log('codes:'.padEnd(10, ' '), myEncodeURI(testArr[1]));
console.log('encodeURI:', encodeURI(testArr[1]))

图片描述

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消