9

极简 Node.js 入门 - 4.1 stream 学习第一步:Buffer

 3 years ago
source link: https://www.cnblogs.com/dolphinX/p/13722472.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Buffer

极简 Node.js 入门系列教程:https://www.yuque.com/sunluyong/node

本文更佳阅读体验:https://www.yuque.com/sunluyong/node/buffer

计算机就是处理 0 和 1,很尴尬的是在引入 TypedArray 之前,JavaScript 没有操作二进制数据流的机制,Buffer 类用一种更适合 Node.js 的方式实现了 Uint8Array API,用于在 TCP 流、文件系统操作等场景处理二进制字节

bit 与 Byte

  1. bit 是我们常说的比特,比特币就是以此命名的,bit 是二进制的最小信息单位,1 bit 就是我们说的 1位,64 位操作系统 CPU一次能处理 5212463e37406b73b693fe832f7bc8c2.svg#card=math&code=2%5E%7B64%7D&height=16&width=19 位的数据
  2. Byte 被翻译为字节,是计量存储或者传输流量的单位,硬盘容量、网速等说的都是字节,一个英文字符是一个字节,也就是我们说的 1B,中文字符通常是两个字节(Node.js 中使用三个字节)
1 byte = 8 bit

而 Buffer 处理的是字节b0dc5e05c01fe45ec018dee390cf449c.svg#card=math&code=2%5E8%3D256&height=16&width=56 , 从 0 开始计数,Buffer 中的 255 标识一个每位都是 1 的字节

Buffer 特性

Buffer 类的实例类似于 0 到 255 之间的整型数组(其他整数会通过 & 255 操作强制转换到此范围),Buffer 是一个 JavaScript 和 C++ 结合的模块,对象内存不经 V8 分配,而是由 C++ 申请、JavaScript 分配。缓冲区的大小在创建时确定,不能调整。

Buffer 对象实在过于常用,被直接内置到全局变量中,使用时候无需 require 引入

实例化 Buffer

在 Node.js v6 之前都是通过调用构造函数的方式实例化 Buffer,根据参数返回不同结果。处于安全性原因,这种方式在 v6 后的版本中已经被废除,现在提供了三个职责清晰的函数处理实例化 Buffer 的工作

Buffer.from

Buffer.from 支持四种参数类型

  • Buffer.from(string [, encoding]):返回一个包含给定字符串的 Buffer
  • Buffer.from(buffer):返回给定 Buffer 的一个副本 Buffer
  • Buffer.from(array):返回一个内容包含所提供的字节副本的 Buffer,数组中每一项是一个表示八位字节的数字,所以值必须在 0 ~ 255 之间,否则会取模
  • Buffer.from(arrayBuffer):返回一个与给定的 ArrayBuffer 共享内存的新 Buffer
  • Buffer.from(object[, offsetOrEncoding[, length]]):取 object 的 valueOf  或 Symbol.toPrimitive 初始化 Buffer
// 16 进制显示值

const buf1 = Buffer.from('test', 'utf-8'); // <Buffer 74 65 73 74>

const buf2 = Buffer.from(buf1); // <Buffer 74 65 73 74>,buf1 副本,修改 buf2 不会影响 buf1

const buf3 = Buffer.from([256, 2, 3]); // <Buffer 00 02 03>,超过 255 会取模

const arr = new Uint16Array(2);
arr[0] = 5000;
arr[1] = 4000;

// 和 arr 共享内存
const buf4 = Buffer.from(arr.buffer);
console.log(buf4); // <Buffer 88 13 a0 0f>

// 修改 arr
arr[1] = 6000;
console.log(buf4); // <Buffer 88 13 70 17> buf4 也受到影响

const buf = Buffer.from(new String('this is a test'));
// <Buffer 74 68 69 73 20 69 73 20 61 20 74 65 73 74>

Buffer.alloc(size [, fill [, encoding]])

分配一个大小为 size 字节的新 Buffer,如果 fill 为 undefined,则用 0 填充 Buffer

  • size 新 Buffer 的所需长度
  • fill | | | 用于预填充新 Buffer 的值。默认值: 0
  • encoding 如果 fill 是一个字符串,则这是它的字符编码。默认值: utf8
const buf = Buffer.alloc(5);
console.log(buf); // <Buffer 00 00 00 00 00>

如果指定了 fill,则分配的 Buffer 通过调用 buf.fill(fill)  进行初始化。

const buf = Buffer.alloc(5, 'a');
console.log(buf); // <Buffer 61 61 61 61 61>

Buffer.allocUnsafe(size)

分配一个大小为 size 字节的新 Buffer,allocUnsafe 执行速度比 alloc 快,但方法的名字听起来很不安全,确实也不安全

const buf = Buffer.allocUnsafe(10);
console.log(buf); // 打印内容不确定

Buffer 模块会预分配一个内部的大小为 Buffer.poolSize 的 Buffer 实例,作为快速分配的内存池,用于使用 allocUnsafe() 创建新的 Buffer 实例


alloc 永远不会使用内部的 Buffer 池,而 allocUnsafe 在 size 小于或等于 Buffer.poolSize 的一半时将会使用内部的 Buffer池。 当调用 allocUnsafe 时分配的内存段尚未初始化(不归零),这样分配内存速度很块,但分配到的内存片段可能包含旧数据。如果在使用的时候不覆盖这些旧数据就可能造成内存泄露,虽然速度快,尽量避免使用

Buffer.allocUnsafeSlow(size)

创建一个大小为 size 字节的新 Buffer,直接通过c++进行内存分配,不会进行旧值填充,当分配的空间小于4KB的时候,allocUnsafe 会直接使用预分配的 Buffer,因此速度比 allocUnsafeSlow 要快,当大于等于4KB的时候二者速度相差无异

Buffer 目前支持以下几种编码格式

  • ascii
  • utf16le
  • base64
  • binary

Buffer 和 string 转换

Buffer.from(string [, encoding]) 字符串转为 Buffer

const buf1 = Buffer.from('test', 'utf-8'); // <Buffer 74 65 73 74>

buf.toString([encoding[, start[, end]]])  Buffer 实例的 toString 方法可以将 Buffer 转为字符串

const buf1 = Buffer.from('test', 'utf-8');
console.log(buf1.toString()); // test
console.log(buf1.toString('hex')); // 74657374

Buffer 拼接

Buffer.concat(list[, totalLength]) 方法可以把多个 Buffer 实例拼接为一个 Buffer 实例

const buf1 = Buffer.from('a');
const buf2 = Buffer.from('b');
const buf3 = Buffer.from('c');

const buf = Buffer.concat([buf1, buf2, buf3]);
console.log(buf); // <Buffer 61 62 63>

StringDecoder

在 NodeJS 中一个汉字由三个字节表示,如果我们处理中文字符的时候使用了不是3的倍数的字节数就会造成字符拼接乱码问题

const buf = Buffer.from('中文字符串!');

for(let i = 0; i < buf.length; i+=5){
  var b = Buffer.allocUnsafe(5);
  buf.copy(b, 0, i);
  console.log(b.toString());
}

这样可以看到结果中出现了乱码
image.png

使用 string_decoder 模块可以解决这个问题

const StringDecoder = require('string_decoder').StringDecoder;
const decoder = new StringDecoder('utf8');
const buf = Buffer.from('中文字符串!');
for(let i = 0; i < buf.length; i+=5){
  var b = Buffer.allocUnsafe(5);
  buf.copy(b, 0, i);
  console.log(decoder.write(b));
}

StringDecoder 在得到编码后,知道宽字节在 utf-8 下占3个字节,所以在处理末尾不全的字节时,会保留到第二次 write()。目前只能处理UTF-8、Base64 和 UCS-2/UTF-16LE。

Buffer 其它常用 API

还有一些 Buffer 常用的 API,比较简单不用代码示例了

  • Buffer.isBuffer:判断对象是否为 Buffer
  • Buffer.isEncoding:判断 Buffer 对象编码
  • buf.length:返回 内存为此 Buffer 实例所申请的字节数,并不是 Buffer 实例内容的字节数
  • buf.indexOf:和数组的 indexOf 类似,返回某字符串、acsii 码或者 buf 在改 buf 中的位置
  • buf.copy:将一个 buf 的(部分)内容复制到另外一个 buf 中

了解了这些内容就可以进入 stream 的学习了


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK