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

2023-12-18 15:38

本文主要是介绍聊聊编码那些事,顺带实现base64,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

  • 进制间的转换

    • 对任意进制的数进行任意进制转换
    • 将任意进制数转换为十进制数
    • 几道关于parseInt的面试题
  • 编码发展历史
  • base64编码

    • 为什么需要base64
  • 如何实现base64

    • 读取buffer转为json对象
    • 将10进制转为2进制
    • 将2进制拼一起3*8然后分隔成4*6
    • 然后将2进制转成10进制
    • base64码
    • 取到每一个base64码
  • 小结

前言

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

进制间的转换

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

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

Number.prototype.toString(radix)

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

parseInt(string, radix)

几道关于parseInt的面试题

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

// 会输出什么?
[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 = 6let flag = 0while (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

这篇关于聊聊编码那些事,顺带实现base64的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/508948

相关文章

Spring Boot 实现 IP 限流的原理、实践与利弊解析

《SpringBoot实现IP限流的原理、实践与利弊解析》在SpringBoot中实现IP限流是一种简单而有效的方式来保障系统的稳定性和可用性,本文给大家介绍SpringBoot实现IP限... 目录一、引言二、IP 限流原理2.1 令牌桶算法2.2 漏桶算法三、使用场景3.1 防止恶意攻击3.2 控制资源

springboot下载接口限速功能实现

《springboot下载接口限速功能实现》通过Redis统计并发数动态调整每个用户带宽,核心逻辑为每秒读取并发送限定数据量,防止单用户占用过多资源,确保整体下载均衡且高效,本文给大家介绍spring... 目录 一、整体目标 二、涉及的主要类/方法✅ 三、核心流程图解(简化) 四、关键代码详解1️⃣ 设置

Nginx 配置跨域的实现及常见问题解决

《Nginx配置跨域的实现及常见问题解决》本文主要介绍了Nginx配置跨域的实现及常见问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来... 目录1. 跨域1.1 同源策略1.2 跨域资源共享(CORS)2. Nginx 配置跨域的场景2.1

Python中提取文件名扩展名的多种方法实现

《Python中提取文件名扩展名的多种方法实现》在Python编程中,经常会遇到需要从文件名中提取扩展名的场景,Python提供了多种方法来实现这一功能,不同方法适用于不同的场景和需求,包括os.pa... 目录技术背景实现步骤方法一:使用os.path.splitext方法二:使用pathlib模块方法三

CSS实现元素撑满剩余空间的五种方法

《CSS实现元素撑满剩余空间的五种方法》在日常开发中,我们经常需要让某个元素占据容器的剩余空间,本文将介绍5种不同的方法来实现这个需求,并分析各种方法的优缺点,感兴趣的朋友一起看看吧... css实现元素撑满剩余空间的5种方法 在日常开发中,我们经常需要让某个元素占据容器的剩余空间。这是一个常见的布局需求

HTML5 getUserMedia API网页录音实现指南示例小结

《HTML5getUserMediaAPI网页录音实现指南示例小结》本教程将指导你如何利用这一API,结合WebAudioAPI,实现网页录音功能,从获取音频流到处理和保存录音,整个过程将逐步... 目录1. html5 getUserMedia API简介1.1 API概念与历史1.2 功能与优势1.3

Java实现删除文件中的指定内容

《Java实现删除文件中的指定内容》在日常开发中,经常需要对文本文件进行批量处理,其中,删除文件中指定内容是最常见的需求之一,下面我们就来看看如何使用java实现删除文件中的指定内容吧... 目录1. 项目背景详细介绍2. 项目需求详细介绍2.1 功能需求2.2 非功能需求3. 相关技术详细介绍3.1 Ja

使用Python和OpenCV库实现实时颜色识别系统

《使用Python和OpenCV库实现实时颜色识别系统》:本文主要介绍使用Python和OpenCV库实现的实时颜色识别系统,这个系统能够通过摄像头捕捉视频流,并在视频中指定区域内识别主要颜色(红... 目录一、引言二、系统概述三、代码解析1. 导入库2. 颜色识别函数3. 主程序循环四、HSV色彩空间详解

PostgreSQL中MVCC 机制的实现

《PostgreSQL中MVCC机制的实现》本文主要介绍了PostgreSQL中MVCC机制的实现,通过多版本数据存储、快照隔离和事务ID管理实现高并发读写,具有一定的参考价值,感兴趣的可以了解一下... 目录一 MVCC 基本原理python1.1 MVCC 核心概念1.2 与传统锁机制对比二 Postg

SpringBoot整合Flowable实现工作流的详细流程

《SpringBoot整合Flowable实现工作流的详细流程》Flowable是一个使用Java编写的轻量级业务流程引擎,Flowable流程引擎可用于部署BPMN2.0流程定义,创建这些流程定义的... 目录1、流程引擎介绍2、创建项目3、画流程图4、开发接口4.1 Java 类梳理4.2 查看流程图4