聊聊编码那些事,顺带实现base64

目录

前言

平常工做中,频繁的使用base64取代小图标,以便减小HTTP请求进而达到性能优化的目的。基于此来聊聊编码的发展、为何须要base64以及如何实现base64。此文章首发于聊聊编码那些事,顺带实现base64转载请注明来源。github

进制间的转换

一些前置知识,也涉及到一些经典的面试小题。面试

对任意进制的数进行任意进制转换

Number.prototype.toString(radix)json

将任意进制数转换为十进制数

parseInt(string, radix)数组

几道关于parseInt的面试题

说到parseInt,不得不提到一个颇有意思的面试题promise

// 会输出什么?
[1, 2, 3].map(parseInt)
// => 1, NaN, NaN

map方法第一个参数为函数,函数有三个参数,array.map((item, index, array) => { ... })浏览器

实际上至关于性能优化

function fn (item, index) {
  return parseInt(item, index)
}
[1, 2, 3].map(fn)
// parseInt迭代过程至关于以下
// parseInt(1, 0) => 1
// parseInt(2, 1) => NaN
// parseInt(3, 2) => NaN

再来看一个相似的面试题

// 会输出什么?
'1 2 3'.replace(/\d/g, parseInt)
// => 1, NaN, 3

replace方法第二个参数如果一个函数,函数会有若干个参数。第一个为匹配模式的字符串;第二个为与模式中子表达式匹配的字符串,能够有零个或多个这样的参数。

实际上至关于以下

function fn (...args) {
  // 只会取前两个参数
  return parseInt(args[0], args[1])
}
'1 2 3'.replace(/\d/g, fn)
// parseInt迭代过程至关于以下
// parseInt('1', 0) => 1
// parseInt('2', 2) => NaN
// parseInt('3', 4) => 3

其实在mdn中对parseInt/map/replace已经讲解的很详细,指望你们在工做之余不要太过浮躁,别作伸手党,静下心来啃一下文档并多作实践,不少面试题天然会迎刃而解。

编码发展历史

ASCII

GBK2312

GBK

GB18030/DBCS

Unicode

UTF-8

如今的标准,有以下特色

  • UTF-8 就是在互联网上使用最广的一种 Unicode 的实现方式
  • UTF-8就是每次以8个位为单位传输数据
  • 而UTF-16就是每次 16 个位
  • UTF-8 最大的一个特色,就是它是一种变长的编码方式
  • Unicode 一个中文字符占 2 个字节,而 UTF-8 一个中文字符占 3 个字节
  • UTF-8 是 Unicode 的实现方式之一

base64编码

为何须要base64

在开发时,常常会有一些小图标图片,每个图片都会有一次HTTP请求,因为浏览器对同一个域名的并发数量有限制,因此咱们应该尽量减小HTTP请求个数。

本文主要讲解编码相关,那就只讲解从编码入手如何去减小HTTP请求。

在计算机内部,任何信息最终都是使用一系列二进制存储,图片也不例外。

并且在img标签的src属性后跟上一个base64字符,若是该字符有效,那么会正常显示图片。

如何实现base64

如下涉及的全部代码均在仓库中,感兴趣的能够自取。

读取buffer转为json对象

首先准备一个2.txt文件。

冯兰兰啊我说今晚月色这么美,你说是的。

case.js代码

const fs = require('mz/fs')
const path = require('path')

// 读取成buffer对象
async function read2JSON () {
   let ret = await fs.readFile(path.resolve(__dirname, '2.txt'))
   console.log(ret.toJSON())
   return ret.toJSON()
}
read2JSON()
// => { type: 'Buffer', data: [ 229, 134, 175, 229... ] }

上面的依赖mz/fs已经将fs都包装成promise,因此咱们能写的更像同步。

readFile函数若是第二个参数没有指定会读取成一个buffer流,是由一个个16进制数组合在一块儿的。

buffer.toJSON能够将一个buffer流转为一个json对象,十六进制也会被转十进制。如上输出所示。

将10进制转为2进制

十进制转为二进制能够经过Number.toString(2)方法

// 将10进制转为2进制
async function data2b () {
  let data = await read2JSON()
  let ret = []
  data.data.forEach(item => {
    ret.push(item.toString(2))
  })
  console.log(ret)
  return ret
}
data2b()
// => [ '11100101', '10000110', '10101111', '11100101'...]

将2进制拼一块儿3*8而后分隔成4*6

一个汉字在UTF-8规范中由三个字节组成,一个字节由8个二进制物理位构成。因此一个汉字实际占用内存3*8base64中咱们实际须要6个物理位表示一个字节即2**6,因此作从新分割4*6

async function split () {
  let data = await data2b()
  let dataStr = data.join('')
  let ret = []

  let splitUnit = 6
  let flag = 0
  while (flag < dataStr.length) {
    ret.push(dataStr.substr(flag, splitUnit))
    flag = flag + splitUnit
  }
  console.log(ret)
  return ret
}
split()
// => [ '111001', '011000', '011010', '101111'...]

而后将2进制转成10进制

二进制转为十进制能够经过parseInt(string, 2)方法

async function data20 () {
  let data = await split()
  let ret = data.map(item => {
    return parseInt(item, 2)
  })
  console.log(ret)
  return ret
}
data20()
// => [ 57, 24, 26, 47, 57, 24, 22, 48, 57, 24, 22, 48 ]

base64码

base64中的64其实是根据2**6所来,表示则由大写字母、小写字母、数字、+/构成。

const lowerCases = 'abcdefghijklmnopqrstuvwxyz'
const numbers = '0123456789'
const base64lib = `${lowerCases.toUpperCase()}${lowerCases}${numbers}+/`
console.log(base64lib)
// => ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/

取到每个base64码

而后咱们则能够取到每个base64

async function main () {
  let data = await data20()
  let ret = []
  data.forEach(item => {
    ret.push(base64lib[item])
  })
  console.log(ret.join(''))
  return ret.join()
}

main()
// => 5Yav5YWw5YWw5ZWK5oiR6K+05LuK5pma5pyI6Imy6L+Z5LmI576O77yM5L2g6K+05piv55qE44CC

咱们能够前往base64在线转码解码进行验证。

encoding-base64-decode

简化代码

对以上思路进行代码简化

const CHARTS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
function transfer (str) {
  let buf = Buffer.from(str)
  let result = ''
  for(let b of buf){
    result += b.toString(2)
  }
  return result.match(/(\d{6})/g).map(val => parseInt(val, 2)).map(val => CHARTS[val]).join('')
}
let fl = transfer('冯')
console.log(fl) // => 5Yav

小结

如上咱们能够实现将中文转为base64,同理咱们也能够转换图片。

async function read2JSON () {
  // let ret = await fs.readFile(path.resolve(__dirname, '2.txt'))
  // 读取图片
  let ret = await fs.readFile(path.resolve(__dirname, '../assets/encoding-base64-example.png'))
  console.log(ret.toJSON())
  return ret.toJSON()
}

特别的: 因为将2进制拼一块儿3*8而后分隔成4*6,原来存储一个汉字须要三个字节,如今须要四个字节存储,因此转换为base64后会比以前大3/1

下面笑脸图片则是由img的src属性展现的,不过本文并无实现图片转base64,由于其逻辑较为复杂,可是本文讲解了大体思路,感兴趣的可再作深究。

encoding-base64-example

encoding-base64-code

相关文章
相关标签/搜索