JS 简单实现UTF-8编码,Base64编码

文章用JS简单的实现UTF-8编码和Base64编码,阅读本文能够了解Unicode 与 UTF-8 之间的转换,了解Base64编码为何会使数据量变长。express

概要:bash

  • Unicode简单了解
  • UTF-8编码
  • Base64编码
  • 总结

Unicode,ASCII,GB2312编码集合等,相似于字典。字符的编码,相似于字典中的字在哪一页哪一行。当不一样系统用同一本字典查同一个编码获得的字符会一致。学习

以下图:ui

1. Unicode简单了解

wikipedia:
编码

Unicode is a computing industry standard for the consistent encoding, representation, and handling of text expressed in most of the world's writing systems.spa

在创造Unicode以前各类语言有不一样的编码集合,ASCII,GB2312等也是发展过程当中编码集合,并且这些编码集合相互冲突,给不一样语言系统进行交流带来了麻烦。由于两种相同的字符在不一样的编码系统中可能有彻底不一样的意思。因而Unicode出现了,Unicode编码集合给每一个字符提供了一个惟一的数字,不论平台,程序,语言,Unicode 字符集所以被普遍应用。3d

Javascript程序是用Unicode字符集编写的, 字符串(string)中每一个字符一般来自于Unicode 字符集。rest

Unicode 字符集相似于字典,字符就相似于字。字符的Unicode码值,就相似于字在字典的第页第几行。code

2. UTF-8编码

2.1为什么有了Unicode字符集还须要 一个编码来传输了?

由于Unicode 编码转换成二进制,是一串0,和1,传输个另外一方的时候,须要一个规则来分割这一串0、1。orm

因而就出现了UTF-n 编码们。

8bit = 1byte

资料

UTF(Universal Transformation Format,通用传输格式),其实就是不改变字符集中各个字符的代码,创建一套新的编码方式,把字符的代码经过这个编码方式映射成传输时的编码,最主要的任务就是在使用Unicode字符集保持通用性的同时节约流量和硬盘空间。

存储
Unicode是一个符号集,规定了符号的二进制代码,没有规定这个二进制代码应该如何存储(即占用多少个字节)因此出现了不一样的存储实现方式。
UTF-32

字符用四个字节表示

UTF-16

字符用两个字节或四个字节表示

UTF-8

一种变长的编码方式,根据须要用1~4个字节来表示字符,(按需传递节约流量和硬盘空间,所以UTF-8用的比较广)

2.2UTF-8编码规则

  • 对于单字节的符号,字节的第一位设为0,后面7位为这个符号的 Unicode 码。
  • 对于n字节的符号(n > 1),第一个字节的前n位都设为1,第n+ 1位设为0,后面字节的前两位设为10。剩下,所有为这个符号的 Unicode 编码。


eg: 字符罗的UTF-8编码

用codePointAt获得了字符的Unicode 编码,确认用几个字节表示,而后按照规则填充。

编码流程:


资料补充:

ES6 提供了codePointAt()方法,可以正确处理字节储存的字符,返回一个字符的码点(Unicode 编码)。

ES6 提供了String.fromCodePoint()方法能正确处理一个码点(Unicode 编码),返回码点(Unicode 编码)对应的字符

2.3UTF-8编码解码简单实现

// 编码
function encodeUtf8(str) {
  var bytes = []
  for (ch of str) {
    // for...of循环,能正确识别 32 位的 UTF-16 字符, 能够查阅资料了解。
    let code = ch.codePointAt(0)
    if (code >= 65536 && code <= 1114111) {// 位运算, 补齐8位
      bytes.push((code >> 18) | 0xf0)
      bytes.push(((code >> 12) & 0x3f) | 0x80)
      bytes.push(((code >> 6) & 0x3f) | 0x80)
      bytes.push((code & 0x3f) | 0x80)
    } else if (code >= 2048 && code <= 65535) {
      bytes.push((code >> 12) | 0xe0)
      bytes.push(((code >> 6) & 0x3f) | 0x80)
      bytes.push((code & 0x3f) | 0x80)
    } else if (code >= 128 && code <= 2047) {
      bytes.push((code >> 6) | 0xc0)
      bytes.push((code & 0x3f) | 0x80)
    } else {
      bytes.push(code)
    }
  }
  return bytes
}
// 补位
function padStart(str, len, prefix) {
  return ((new Array(len + 1).join(prefix)) + str).slice(-len) //  也可用 new Array(len+1).fill(0)
}
// 解码
function decodeUtf8(str) {
  let strValue = ''
  let obStr = [...str].map((ch)=> {
    // 一位16进制数 转二进制 须要四位来表示  补全位数
    return padStart(parseInt(ch,16).toString(2), 4, 0)
  }).join('').match(/\d{8}/g).map((item)=> parseInt(item,2))
  for (var i = 0; i < obStr.length; ) {
    
    let code = obStr[i]
    let code1, code2, code3, code4, hex
    // 比较 前4 位 parseInt(11110000,2) = 240 4个字节
    if ((code & 240) == 240) {
      code1 = (code & 0x03).toString(2)
      code2 = padStart((obStr[i + 1] & 0x3f).toString(2),6, '0')
      code3 = padStart((obStr[i + 2] & 0x3f).toString(2),6, '0')
      code4 = padStart((obStr[i + 3] & 0x3f).toString(2),6, '0')
      hex = parseInt((code1 + code2 + code3 + code4),2)
      strValue = strValue + String.fromCodePoint(hex)
      i = i + 4
    } else if ((code & 224) == 224) { // 3个字节表
      code1 = (code & 0x07).toString(2)
      code2 = padStart((obStr[i + 1] & 0x3f).toString(2),6, '0')
      code3 = padStart((obStr[i + 2]& 0x3f).toString(2),6, '0')
      hex = parseInt((code1 + code2 + code3),2)
      strValue = strValue + String.fromCodePoint(hex)
      i = i + 3
    } else if ((code & 192) == 192) {// 2个字节表
      code1 = (code & 0x0f).toString(2)
      code2 = padStart((obStr[i + 1] & 0x3f).toString(2),6, '0')
      hex = parseInt((obStr + code2),2)
      strValue = strValue + String.fromCodePoint(hex)
      i = i + 2
    } else {
      hex = code
      strValue = strValue + String.fromCodePoint(code)
      i = i + 1
    }
  }
  return strValue
}
// byte to hex
function transferHex(bytes) {
  let s = ''
  bytes &&
    bytes.forEach(ch => {
      s = s + ch.toString(16)
    })
  return s
}
let text = "罗小步 啊哈哈 𠮷 ssdf 34534 ASD"
let strHax = transferHex(encodeUtf8(text))
console.log(strHax)
let str = decodeUtf8(strHax)
console.log(str)

console.log("test ok?", text === str)复制代码

3. Base64编码

3.1 Base64编码规则

规则:Base64的编码方法要求把每三个8bit的字节转换成四个6bit的字节,而后把6Bit再添两位高位0,组成四个8Bit的字节。

若是要编码的二进制数据不是3的倍数,最后剩下一个或者两个字节Base64会在末尾补零,再在编码的末尾加上一个或者两个‘=’。

每一个8bit 编码成:CHARST[paresInt(8bit ,2)]

CHARTS = ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/复制代码

编码流程:

3.2 Base64编码解码简单实现

const CHARTS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
const prefix = '='
const prefixTwo = 2
const prefixfour = 4
function padEnd(str, len, prefix) {
  return (str + (new Array(len + 1)).join(prefix)).slice(0, len)
}
function padStart(str, len, prefix) {
  return ((new Array(len + 1).join(prefix)) + str).slice(-len)
}
// 编码
function encodeBase64(str){
  let byteStr = ''
  for(let ch of encodeUtf8(str)){ 
    byteStr = byteStr + padStart(ch.toString(2),8,0)
  }
  let rest = byteStr.length % 6 // 余2 就是剩下了一个字节,余 4 就是剩下两个字节
  let restStr = rest === prefixTwo ? '==' :'='
  let prefixzero = rest === prefixTwo ? prefixfour: prefixTwo
  byteStr = padEnd(byteStr , byteStr.length + prefixzero,'0')
  return byteStr.match(/(\d{6})/g).map(val=>parseInt(val,2)).map(val=>CHARTS[val]).join('') + restStr;
}
// 解码
function decodeBase64(str) {
  let matchTime = str.match(/(ha)/g)
  
  let [...restStr] = str.replace(/=/g,'')
  restStr = restStr.map((item)=> {
    let value = CHARTS.indexOf(item)
    return padStart(value.toString(2),6,0)
  }).join('').match(/(\d{8})/g).map((item)=>parseInt(item,2).toString(16)).join()
  console.log(restStr)
  return decodeUtf8(restStr)
}

let text = "罗小步 啊哈哈 𠮷 ssdf 34534 ASD"
let strHax = encodeBase64(text)
console.log(strHax)
let str = decodeBase64(strHax)
console.log(str)

console.log("test ok?", text === str)复制代码

Base64的编码方法要求把每三个8bit的字节转换成四个6bit的字节,编码会使数据量变长原来的1/3.

4. 总结

编码方式只是一种对字符集表现的形式。文章用js 简单的实现utf8编码和base64编码。代码实现比较粗糙,理解不许确之处,还请教正。欢迎一块儿讨论学习。

相关文章
相关标签/搜索