NodeJS模块Buffer原理及使用方法解析

Buffer 作为 nodejs 中重要的概念和功能,为开发者提供了操作二进制的能力。本文记录了几个问题,来加深对 Buffer 的理解和使用:

  • 认识缓冲器
  • 如何申请堆外内存
  • 如何计算字节长度
  • 如何计算字节长度
  • 如何转换字符编码
  • 理解共享内存与拷贝内存

认识 Buffer(缓冲器)

Buffer 是 nodejs 核心 API,它提供我们处理二进制数据流的功能。Buffer 的使用和 ES2017 的 Uint8Array 非常相似,但由于 node 的特性,专门提供了更深入的 api。

Uint8Array 的字面意思就是:8 位无符号整型数组。一个字节是 8bit,而字节的表示也是由两个 16 进制(4bit)的数字组成的。

const buf = Buffer.alloc(1);
console.log(buf); // output: <Buffer 00>

如何申请堆外内存

Buffer 可以跳出 nodejs 对堆内内存大小的限制。nodejs12 提供了 4 种 api 来申请堆外内存:

  • Buffer.from()
  • Buffer.alloc(size[, fill[, encoding]])
  • Buffer.allocUnsafe(size)
  • Buffer.allocUnsafeSlow(size)

Buffer.alloc vs Buffer.allocUnsafe

在申请内存时,可能这片内存之前存储过其他数据。如果不清除原数据,那么会有数据泄漏的安全风险;如果清除原数据,速度上会慢一些。具体用哪种方式,根据实际情况定。

  • Buffer.alloc:申请指定大小的内存,并且清除原数据,默认填充 0
  • Buffer.allocUnsafe:申请指定大小内存,但不清除原数据,速度更快

根据提供的 api,可以手动实现一个alloc:

function pollifyAlloc(size, fill = 0, encoding = "utf8") {
  const buf = Buffer.allocUnsafe(size);
  buf.fill(fill, 0, size, encoding);
  return buf;
}

Buffer.allocUnsafe vs Buffer.allocUnsafeSlow

从命名上可以直接看出效果,Buffer.allocUnsafeSlow更慢。因为当使用 Buffer.allocUnsafe 创建新的 Buffer 实例时,如果要分配的内存小于 4KB,则会从一个预分配的 Buffer 切割出来。 这可以避免垃圾回收机制因创建太多独立的 Buffer 而过度使用。

这种方式通过消除跟踪和清理的需要来改进性能和内存使用。

如何计算字节长度

利用 Buffer,可以获得数据的真实所占字节。例如一个汉字,它的字符长度是 1。但由于是 utf8 编码的汉字,所以占用 3 个字节。

直接利用Buffer.byteLength()可以获得字符串指定编码的字节长度:

const str = "本文原文地址: xxoo521.com";

console.log(Buffer.byteLength(str, "utf8")); // output: 31
console.log(str.length); // output: 19

也可以直接访问 Buffer 实例的 length 属性(不推荐):

console.log(Buffer.from(str, "utf8").length); // output: 31

如何转换字符编码

Nodejs 当前支持的编码格式有:ascii、utf8、utf16le、ucs2、base64、latin1、binary、hex。其他编码需要借助三方库来完成。

下面,是用Buffer.from()和buf.toString()来封装的 nodejs 平台的编码转换函数:

function trans(str, from = "utf8", to = "utf8") {
  const buf = Buffer.from(str, from);
  return buf.toString(to);
}

// output: 5Y6f5paH5Zyw5Z2AOiB4eG9vNTIxLmNvbQ==
console.log(trans("原文地址: xxoo521.com", "utf8", "base64"));

共享内存与拷贝内存

在生成 Buffer 实例,操作二进制数据的时候,千万要注意接口是基于共享内存,还是基于拷贝底层内存。

例如对于生成 Buffer 实例的from(),不同类型的参数,nodejs 底层的行为是不同的。

为了更形象地解释,请看下面两段代码。

代码 1:

const buf1 = Buffer.from("buffer");
const buf2 = Buffer.from(buf1); // 拷贝参数中buffer的数据到新的实例
buf1[0]++;

console.log(buf1.toString()); // output: cuffer
console.log(buf2.toString()); // output: buffer

代码 2:

const arr = new Uint8Array(1);
arr[0] = 97;

const buf1 = Buffer.from(arr.buffer);
console.log(buf1.toString()); // output: a

arr[0] = 98;
console.log(buf1.toString()); // output: b

在第二段代码中,传入Buffer.from的参数类型是arrayBuffer。因此Buffer.from仅仅是创建视图,而不是拷贝底层内存。buf1 和 arr 的内存是共享的。

在操作 Buffer 的过程中,需要特别注意共享和拷贝的区别,发生错误比较难排查。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 详细了解JAVA NIO之Buffer(缓冲区)

    当我们需要与 NIO Channel 进行交互时, 我们就需要使用到 NIO Buffer, 即数据从 Buffer读取到 Channel 中, 并且从 Channel 中写入到 Buffer 中.缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存.这块内存被包装成NIO Buffer对象,并提供了一组方法,用来方便的访问该块内存. 缓冲区基础 Buffer 类型有: 缓冲区是包在一个对象内的基础数据的数组,Buffer类相比一般简单数组而言其优点是将数据的内容和相关信息放在一个对象里面

  • 浅析我对 String、StringBuilder、StringBuffer 的理解

    StringBuilder.StringBuffer 和 String 一样,都是用于存储字符串的. 1.那既然有了 String ,为什么还需要他们两个呢? 原因是 String 是不可变的,它每次的字符串拼接,实际上都会 new 一个新的 String 进行接收. 2.谈谈StringBuilder.StringBuffer他们两个的联系: 我们可以知道 StringBuffer 在 1.0 的时候就发布了,那为什么还需要 StringBuilder 呢?原因是它的大部分方法都上了锁,是线程

  • NodeJS中Buffer模块详解

    一,开篇分析 所谓缓冲区Buffer,就是 "临时存贮区" 的意思,是暂时存放输入输出数据的一段内存. JS语言自身只有字符串数据类型,没有二进制数据类型,因此NodeJS提供了一个与String对等的全局构造函数Buffer来提供对二进制数据的操作.除了可以读取文件得到Buffer的实例外,还能够直接构造,例如: 复制代码 代码如下: var buffer = new Buffer([ 0x68, 0x65, 0x6c, 0x6c, 0x6f ]) ; Buffer与字符串类似,除了

  • nodejs基础之buffer缓冲区用法分析

    本文实例讲述了nodejs基础之buffer缓冲区用法.分享给大家供大家参考,具体如下: JavaScript 语言自身只有字符串数据类型,没有二进制数据类型.但在处理像TCP流或文件流时,必须使用到二进制数据.因此在 Node.js中,定义了一个 Buffer 类,该类用来创建一个专门存放二进制数据的缓存区.在 Node.js 中,Buffer 类是随 Node 内核一起发布的核心库.Buffer 库为 Node.js 带来了一种存储原始数据的方法,可以让 Node.js 处理二进制数据,每当

  • nodejs二进制与Buffer的介绍与使用

    Buffer 介绍 javascript 中对数据处理都是以字符串的形式,而对于二进制数据就不便于处理,所以 Buffer 便是用于读取或操作二进制数据都对象. 一句话概括: Buffer 类是一个全局变量,用于直接处理二进制数据,提供工具类方法. 官网上关于 Buffer 解释的非常清楚,所以就不再拷贝官网,这里列举一些使用较为频繁的场景. 对象转换成Buffer 在操作文件或者IO的场景中,我们需要将对象转换成二进制数据流. 引用官方文档 Buffer.from(array) 返回一个新的

  • python 使用多线程创建一个Buffer缓存器的实现思路

    这几天学习人脸识别的时候,虽然运行的没有问题,但我却意识到了一个问题 在图片进行传输的时候,GPU的利用率为0 也就是说,图片的传输速度和GPU的处理速度不能很好衔接 于是,我打算利用多线程开发一个buffer缓存 实现的思路如下 定义一个Buffer类,再其构造函数中创建一个buffer空间(这里最好使用list类型) 我们还需要的定义线程锁LOCK(数据传输和提取的时候会用到) 因为需要两种方法(读数据和取数据),所以我们需要定义两个锁 实现的代码如下: #-*-coding:utf-8-*

  • Java ByteBuffer网络编程用法实例解析

    做tcp网络编程,要解析一批批的数据,可是数据是通过Socket连接的InputStream一次次读取的,读取到的不是需要转换的对象,而是要直接根据字节流和协议来生成自己的数据对象. 按照之前的编程思维,总是请求然后响应,当然Socket也是请求和响应,不过与单纯的请求响应是不同的. 这里Socket连接往往是要保持住的,也就是长连接,然后设置一个缓冲区,网络流不断的追加到缓冲区.然后后台去解析缓冲区的字节流. 如图所示,网络的流一直在传递,我们收到也许是完成的数据流,也可能是没有传递完的.这里

  • 详解nodeJS之二进制buffer对象

    前面的话 在ES6引入TypedArray之前,JavaScript语言没有读取或操作二进制数据流的机制.Buffer类被引入作为Nodejs的API的一部分,使其可以在TCP流和文件系统操作等场景中处理二进制数据流.现在TypedArray已经被添加进ES6中,Buffer类以一种更优与更适合Node.js用例的方式实现了Uint8Array.本文将详细介绍buffer对象 概述 由于应用场景不同,在Node中,应用需要处理网络协议.操作数据库.处理图片.接收上传文件等,在网络流和文件的操作中

  • NodeJS模块Buffer原理及使用方法解析

    Buffer 作为 nodejs 中重要的概念和功能,为开发者提供了操作二进制的能力.本文记录了几个问题,来加深对 Buffer 的理解和使用: 认识缓冲器 如何申请堆外内存 如何计算字节长度 如何计算字节长度 如何转换字符编码 理解共享内存与拷贝内存 认识 Buffer(缓冲器) Buffer 是 nodejs 核心 API,它提供我们处理二进制数据流的功能.Buffer 的使用和 ES2017 的 Uint8Array 非常相似,但由于 node 的特性,专门提供了更深入的 api. Uin

  • Mysql临时表原理及创建方法解析

    这篇文章主要介绍了Mysql临时表原理及创建方法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 mysql 利用 temporary 关键字就可以创建出一个临时表.创建的这张表会在与服务器的会话终止时自动消失 语法:create temporary table tbl_name...; 规则:每个会话只能看到自己创建的临时表,不同的会话可以创建相同表名称的临时表.临时表的表名可以和永久表的名字相同. 好处:可以利用临时表保存一些临时数据,断

  • Python模块zipfile原理及使用方法详解

    zipfile是python里用来做zip格式编码的压缩和解压缩的,由于是很常见的zip格式,所以这个模块使用频率也是比较高的 zipfile里有两个非常重要的class, 分别是ZipFile和ZipInfo, 在绝大多数的情况下,我们只需要使用这两个class就可以了. ZipFile是主要的类,用来创建和读取zip文件 ZipInfo是存储的zip文件的每个文件的信息的. 比如要读取一个zipfile,这里假设filename是一个文件的路径: import zipfile z = zip

  • Java常用线程池原理及使用方法解析

    一.简介 什么是线程池? 池的概念大家也许都有所听闻,池就是相当于一个容器,里面有许许多多的东西你可以即拿即用.java中有线程池.连接池等等.线程池就是在系统启动或者实例化池时创建一些空闲的线程,等待工作调度,执行完任务后,线程并不会立即被销毁,而是重新处于空闲状态,等待下一次调度. 线程池的工作机制? 在线程池的编程模式中,任务提交并不是直接提交给线程,而是提交给池.线程池在拿到任务之后,就会寻找有没有空闲的线程,有则分配给空闲线程执行,暂时没有则会进入等待队列,继续等待空闲线程.如果超出最

  • PHP unset函数原理及使用方法解析

    unset-释放给定的变量 说明 unset(mixed$var[,mixed$...] ) :void unset()销毁指定的变量. unset()在函数中的行为会依赖于想要销毁的变量的类型而有所不同. 如果在函数中unset()一个全局变量,则只是局部变量被销毁,而在调用环境中的变量将保持调用unset()之前一样的值. <?php function destroy_foo() { global $foo; unset($foo); } $foo = 'bar'; destroy_foo(

  • Python -m参数原理及使用方法解析

    python -m xxx.py 作用是:把xxx.py文件当做模块启动 但是我一直不明白当做模块启动到底有什么用.python xxx.py和python -m xxx.py有什么区别! 自问自答: python xxx.py python -m xxx.py 这是两种加载py文件的方式: 1叫做直接运行 2把模块当作脚本来启动(注意:但是__name__的值为'main' ) 不同的加载py文件的方式,主要是影响--sys.path 这个属性.sys.path 就相当于liunx中的PATH

  • Python特殊属性property原理及使用方法解析

    1 什么是特性property property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值 import math class Circle: def __init__(self,radius): #圆的半径radius self.radius=radius @property def area(self): return math.pi * self.radius**2 #计算面积 @property def perimeter(self): return 2*math.pi*

  • Javascript Symbol原理及使用方法解析

    Symbol是ES6中新引入的一种基本数据类型,在此之前JavaScript中已有几种基本数据类型: Numberg String Boolean Null Undefined Object 不同于其他基本类型的通俗易懂,Symbol 是什么和有什么用一直有些让人困惑. 什么是Symbol JavaScript标准中规定对象的key只能是 String 或 Symbol 类型,区别在于 String 类型的key可以重复而 Symbol 类型的key是唯一的.Symbol 的本质是表示一个唯一标

  • Docker容器数据卷原理及使用方法解析

    什么是容器数据卷 如果数据都在容器中,一旦容器删除,数据就会丢失! eg : mysql容器删了,就是我们常说的删库跑路. 需求:数据可以持久化,即时删掉容器,我们的数据还在 容器直接可以有一个数据共享的技术!Docker容器产生的数据,同步到本地! 这就是卷技术!目录的挂载,将我们容器的目录挂载到linux上面! 总结:卷技术就是为了实现数据的持久化和同步操作,容器间也是可以数据共享的 使用数据卷 方式一:直接使用命令来挂载 -v # 命令 docker run -it -v 主机的目录:容器

  • GTK treeview原理及使用方法解析

    GtkTreeView 构件是一个高级的构件,利用他你就可以制作出漂亮的普通列表或者是树状的列表.这个构件里可以包含一或者多行.他的构架呢?正是采用了大名鼎鼎的MVC (Model View Controller) 设计框架.也就是说数据和显示方式是进行了一种分离的操作. 于是在GtktreeView构件中确实还有着其他几个独立的对象结构(objects). 其中 GtkCellRenderer 就决定了在GtkTreeViewColumn. 中的数据究竟是如何来进行显示呈现的. GtkList

随机推荐