模块概览

Buffer是node的核心模块,开发者可以利用它来处理二进制数据,比如文件流的读写、网络请求数据的处理等。

Buffer的API非常多,本文仅挑选 比较常用/容易理解 的API进行讲解,包括Buffer实例的创建、比较、连接、拷贝、查找、遍历、类型转换、截取、编码转换等。

创建

  • new Buffer(array)
  • Buffer.alloc(length)
  • Buffer.allocUnsafe(length)
  • Buffer.from(array)

通过 new Buffer(array)

// Creates a new Buffer containing the ASCII bytes of the string 'buffer'
const buf = new Buffer([0x62, 0x75, 0x66, 0x66, 0x65, 0x72]);

验证下:

var array = 'buffer'.split('').map(function(v){
return '0x' + v.charCodeAt(0).toString(16)
}); console.log( array.join() );
// 输出:0x62,0x75,0x66,0x66,0x65,0x72

通过 Buffer.alloc(length)

var buf1 = Buffer.alloc(10);  // 长度为10的buffer,初始值为0x0
var buf2 = Buffer.alloc(10, 1); // 长度为10的buffer,初始值为0x1
var buf3 = Buffer.allocUnsafe(10);  // 长度为10的buffer,初始值不确定
var buf4 = Buffer.from([1, 2, 3])  // 长度为3的buffer,初始值为 0x01, 0x02, 0x03

通过Buffer.from()

例子一:Buffer.from(array)

// [0x62, 0x75, 0x66, 0x66, 0x65, 0x72] 为字符串 "buffer"
// 0x62 为16进制,转成十进制就是 98,代表的就是字母 b
var buf = Buffer.from([0x62, 0x75, 0x66, 0x66, 0x65, 0x72]);
console.log(buf.toString());

例子二:Buffer.from(string[, encoding])

通过string创建buffer,跟将buffer转成字符串时,记得编码保持一致,不然会出现乱码,如下所示。

var buf = Buffer.from('this is a tést');  // 默认采用utf8

// 输出:this is a tést
console.log(buf.toString()); // 默认编码是utf8,所以正常打印 // 输出:this is a tC)st
console.log(buf.toString('ascii')); // 转成字符串时,编码不是utf8,所以乱码

对乱码的分析如下:

var letter = 'é';
var buff = Buffer.from(letter); // 默认编码是utf8,这里占据两个字节 <Buffer c3 a9>
var len = buff.length; // 2
var code = buff[0]; // 第一个字节为0xc3,即195:超出ascii的最大支持范围
var binary = code.toString(2); // 195的二进制:10101001
var finalBinary = binary.slice(1); // 将高位的1舍弃,变成:0101001
var finalCode = parseInt(finalBinary, 2); // 0101001 对应的十进制:67
var finalLetter = String.fromCharCode(finalCode); // 67对应的字符:C // 同理 0xa9最终转成的ascii字符为)
// 所以,最终输出为 this is a tC)st

例子三:Buffer.from(buffer)

创建新的Buffer实例,并将buffer的数据拷贝到新的实例子中去。

var buff = Buffer.from('buffer');
var buff2 = Buffer.from(buff); console.log(buff.toString()); // 输出:buffer
console.log(buff2.toString()); // 输出:buffer buff2[0] = 0x61; console.log(buff.toString()); // 输出:buffer
console.log(buff2.toString()); // 输出:auffer

buffer比较

buf.equals(otherBuffer)

判断两个buffer实例存储的数据是否相同,如果是,返回true,否则返回false。

// 例子一:编码一样,内容相同
var buf1 = Buffer.from('A');
var buf2 = Buffer.from('A'); console.log( buf1.equals(buf2) ); // true // 例子二:编码一样,内容不同
var buf3 = Buffer.from('A');
var buf4 = Buffer.from('B'); console.log( buf3.equals(buf4) ); // false // 例子三:编码不一样,内容相同
var buf5 = Buffer.from('ABC'); // <Buffer 41 42 43>
var buf6 = Buffer.from('414243', 'hex'); console.log(buf5.equals(buf6));

buf.compare(target[, targetStart[, targetEnd[, sourceStart[, sourceEnd]]]])

同样是对两个buffer实例进行比较,不同的是:

  1. 可以指定特定比较的范围(通过start、end指定)
  2. 返回值为整数,达标buf、target的大小关系

假设返回值为

  • 0:buf、target大小相同。
  • 1:buf大于target,也就是说buf应该排在target之后。
  • -1:buf小于target,也就是说buf应该排在target之前。

看例子,官方的例子挺好的,直接贴一下:

const buf1 = Buffer.from('ABC');
const buf2 = Buffer.from('BCD');
const buf3 = Buffer.from('ABCD'); // Prints: 0
console.log(buf1.compare(buf1)); // Prints: -1
console.log(buf1.compare(buf2)); // Prints: -1
console.log(buf1.compare(buf3)); // Prints: 1
console.log(buf2.compare(buf1)); // Prints: 1
console.log(buf2.compare(buf3)); // Prints: [ <Buffer 41 42 43>, <Buffer 41 42 43 44>, <Buffer 42 43 44> ]
// (This result is equal to: [buf1, buf3, buf2])
console.log([buf1, buf2, buf3].sort(Buffer.compare));

Buffer.compare(buf1, buf2)

跟 buf.compare(target) 大同小异,一般用于排序。直接贴官方例子:

const buf1 = Buffer.from('1234');
const buf2 = Buffer.from('0123');
const arr = [buf1, buf2]; // Prints: [ <Buffer 30 31 32 33>, <Buffer 31 32 33 34> ]
// (This result is equal to: [buf2, buf1])
console.log(arr.sort(Buffer.compare));

buffer比较

buf.equals(otherBuffer)

判断两个buffer实例存储的数据是否相同,如果是,返回true,否则返回false。

// 例子一:编码一样,内容相同
var buf1 = Buffer.from('A');
var buf2 = Buffer.from('A'); console.log( buf1.equals(buf2) ); // true // 例子二:编码一样,内容不同
var buf3 = Buffer.from('A');
var buf4 = Buffer.from('B'); console.log( buf3.equals(buf4) ); // false // 例子三:编码不一样,内容相同
var buf5 = Buffer.from('ABC'); // <Buffer 41 42 43>
var buf6 = Buffer.from('414243', 'hex'); console.log(buf5.equals(buf6));

buf.compare(target[, targetStart[, targetEnd[, sourceStart[, sourceEnd]]]])

同样是对两个buffer实例进行比较,不同的是:

  1. 可以指定特定比较的范围(通过start、end指定)
  2. 返回值为整数,达标buf、target的大小关系

假设返回值为

  • 0:buf、target大小相同。
  • 1:buf大于target,也就是说buf应该排在target之后。
  • -1:buf小于target,也就是说buf应该排在target之前。

看例子,官方的例子挺好的,直接贴一下:

const buf1 = Buffer.from('ABC');
const buf2 = Buffer.from('BCD');
const buf3 = Buffer.from('ABCD'); // Prints: 0
console.log(buf1.compare(buf1)); // Prints: -1
console.log(buf1.compare(buf2)); // Prints: -1
console.log(buf1.compare(buf3)); // Prints: 1
console.log(buf2.compare(buf1)); // Prints: 1
console.log(buf2.compare(buf3)); // Prints: [ <Buffer 41 42 43>, <Buffer 41 42 43 44>, <Buffer 42 43 44> ]
// (This result is equal to: [buf1, buf3, buf2])
console.log([buf1, buf2, buf3].sort(Buffer.compare));

Buffer.compare(buf1, buf2)

跟 buf.compare(target) 大同小异,一般用于排序。直接贴官方例子:

const buf1 = Buffer.from('1234');
const buf2 = Buffer.from('0123');
const arr = [buf1, buf2]; // Prints: [ <Buffer 30 31 32 33>, <Buffer 31 32 33 34> ]
// (This result is equal to: [buf2, buf1])
console.log(arr.sort(Buffer.compare));

从Buffer.from([62])谈起

这里稍微研究下Buffer.from(array)。下面是官方文档对API的说明,也就是说,每个array的元素对应1个字节(8位),取值从0到255。

Allocates a new Buffer using an array of octets.

数组元素为数字

首先看下,传入的元素为数字的场景。下面分别是10进制、8进制、16进制,跟预期中的结果一致。

var buff = Buffer.from([62])
// <Buffer 3e>
// buff[0] === parseInt('3e', 16) === 62
var buff = Buffer.from([062])
// <Buffer 32>
// buff[0] === parseInt(62, 8) === parseInt(32, 16) === 50
var buff = Buffer.from([0x62])
// <Buffer 62>
// buff[0] === parseInt(62, 16) === 98

数组元素为字符串

再看下,传入的元素为字符串的场景。

  1. 0开头的字符串,在parseInt('062')时,可以解释为62,也可以解释为50(八进制),这里看到采用了第一种解释。
  2. 字符串的场景,跟parseInt()有没有关系,暂未深入探究,只是这样猜想。TODO(找时间研究下)
var buff = Buffer.from(['62'])
// <Buffer 3e>
// buff[0] === parseInt('3e', 16) === parseInt('62') === 62
var buff = Buffer.from(['062'])
// <Buffer 3e>
// buff[0] === parseInt('3e', 16) === parseInt('062') === 62
var buff = Buffer.from(['0x62'])
// <Buffer 62>
// buff[0] === parseInt('62', 16) === parseInt('0x62') === 98

数组元素大小超出1个字节

感兴趣的同学自行探究。

var buff = Buffer.from([256])
// <Buffer 00>

Buffer.from('1')

一开始不自觉的会将Buffer.from('1')[0]"1"划等号,其实"1"对应的编码是49。

var buff = Buffer.from('1')  // <Buffer 31>
console.log(buff[0] === 1) // false

这样对比就知道了,编码为1的是个控制字符,表示 Start of Heading。

console.log( String.fromCharCode(49) )  // '1'
console.log( String.fromCharCode(1) ) // '\u0001'

buffer连接:Buffer.concat(list[, totalLength])

备注:个人觉得totalLength这个参数挺多余的,从官方文档来看,是处于性能提升的角度考虑。不过内部实现也只是遍历list,将length累加得到totalLength,从这点来看,性能优化是几乎可以忽略不计的。

var buff1 = Buffer.alloc(10);
var buff2 = Buffer.alloc(20); var totalLength = buff1.length + buff2.length; console.log(totalLength); // 30 var buff3 = Buffer.concat([buff1, buff2], totalLength); console.log(buff3.length); // 30

除了上面提到的性能优化,totalLength还有两点需要注意。假设list里面所有buffer的长度累加和为length

  • totalLength > length:返回长度为totalLength的Buffer实例,超出长度的部分填充0。
  • totalLength < length:返回长度为totalLength的Buffer实例,后面部分舍弃。
var buff4 = Buffer.from([1, 2]);
var buff5 = Buffer.from([3, 4]); var buff6 = Buffer.concat([buff4, buff5], 5); console.log(buff6.length); //
console.log(buff6); // <Buffer 01 02 03 04 00> var buff7 = Buffer.concat([buff4, buff5], 3); console.log(buff7.length); // 3
console.log(buff7); // <Buffer 01 02 03>

拷贝:buf.copy(target[, targetStart[, sourceStart[, sourceEnd]]])

使用比较简单,如果忽略后面三个参数,那就是将buf的数据拷贝到target里去,如下所示:

var buff1 = Buffer.from([1, 2]);
var buff2 = Buffer.alloc(2); buff1.copy(buff2); console.log(buff2); // <Buffer 01 02>

另外三个参数比较直观,直接看官方例子

const buf1 = Buffer.allocUnsafe(26);
const buf2 = Buffer.allocUnsafe(26).fill('!'); for (let i = 0 ; i < 26 ; i++) {
// 97 is the decimal ASCII value for 'a'
buf1[i] = i + 97;
} buf1.copy(buf2, 8, 16, 20); // Prints: !!!!!!!!qrst!!!!!!!!!!!!!
console.log(buf2.toString('ascii', 0, 25));

拷贝:buf.copy(target[, targetStart[, sourceStart[, sourceEnd]]])

使用比较简单,如果忽略后面三个参数,那就是将buf的数据拷贝到target里去,如下所示:

var buff1 = Buffer.from([1, 2]);
var buff2 = Buffer.alloc(2); buff1.copy(buff2); console.log(buff2); // <Buffer 01 02>

另外三个参数比较直观,直接看官方例子

const buf1 = Buffer.allocUnsafe(26);
const buf2 = Buffer.allocUnsafe(26).fill('!'); for (let i = 0 ; i < 26 ; i++) {
// 97 is the decimal ASCII value for 'a'
buf1[i] = i + 97;
} buf1.copy(buf2, 8, 16, 20); // Prints: !!!!!!!!qrst!!!!!!!!!!!!!
console.log(buf2.toString('ascii', 0, 25));

查找:buf.indexOf(value[, byteOffset][, encoding])

跟数组的查找差不多,需要注意的是,value可能是String、Buffer、Integer中的任意类型。

  • String:如果是字符串,那么encoding就是其对应的编码,默认是utf8。
  • Buffer:如果是Buffer实例,那么会将value中的完整数据,跟buf进行对比。
  • Integer:如果是数字,那么value会被当做无符号的8位整数,取值范围是0到255。

另外,可以通过byteOffset来指定起始查找位置。

直接上代码,官方例子妥妥的,耐心看完它基本就理解得差不多了。

const buf = Buffer.from('this is a buffer');

// Prints: 0
console.log(buf.indexOf('this')); // Prints: 2
console.log(buf.indexOf('is')); // Prints: 8
console.log(buf.indexOf(Buffer.from('a buffer'))); // Prints: 8
// (97 is the decimal ASCII value for 'a')
console.log(buf.indexOf(97)); // Prints: -1
console.log(buf.indexOf(Buffer.from('a buffer example'))); // Prints: 8
console.log(buf.indexOf(Buffer.from('a buffer example').slice(0, 8))); const utf16Buffer = Buffer.from('\u039a\u0391\u03a3\u03a3\u0395', 'ucs2'); // Prints: 4
console.log(utf16Buffer.indexOf('\u03a3', 0, 'ucs2')); // Prints: 6
console.log(utf16Buffer.indexOf('\u03a3', -4, 'ucs2'));

写:buf.write(string[, offset[, length]][, encoding])

将sring写入buf实例,同时返回写入的字节数。

参数如下:

  • string:写入的字符串。
  • offset:从buf的第几位开始写入,默认是0。
  • length:写入多少个字节,默认是 buf.length - offset。
  • encoding:字符串的编码,默认是utf8。

看个简单例子

var buff = Buffer.alloc(4);
buff.write('a'); // 返回 1
console.log(buff); // 打印 <Buffer 61 00 00 00> buff.write('ab'); // 返回 2
console.log(buff); // 打印 <Buffer 61 62 00 00>

填充:buf.fill(value[, offset[, end]][, encoding])

value填充buf,常用于初始化buf。参数说明如下:

  • value:用来填充的内容,可以是Buffer、String或Integer。
  • offset:从第几位开始填充,默认是0。
  • end:停止填充的位置,默认是 buf.length。
  • encoding:如果value是String,那么为value的编码,默认是utf8。

例子:

var buff = Buffer.alloc(20).fill('a');

console.log(buff.toString());  // aaaaaaaaaaaaaaaaaaaa

转成字符串: buf.toString([encoding[, start[, end]]])

把buf解码成字符串,用法比较直观,看例子

var buff = Buffer.from('hello');

console.log( buff.toString() );  // hello

console.log( buff.toString('utf8', 0, 2) );  // he

转成JSON字符串:buf.toJSON()

var buff = Buffer.from('hello');

console.log( buff.toJSON() );  // { type: 'Buffer', data: [ 104, 101, 108, 108, 111 ] }

遍历:buf.values()、buf.keys()、buf.entries()

用于对buf进行for...of遍历,直接看例子。

var buff = Buffer.from('abcde');

for(const key of buff.keys()){
console.log('key is %d', key);
}
// key is 0
// key is 1
// key is 2
// key is 3
// key is 4 for(const value of buff.values()){
console.log('value is %d', value);
}
// value is 97
// value is 98
// value is 99
// value is 100
// value is 101 for(const pair of buff.entries()){
console.log('buff[%d] === %d', pair[0], pair[1]);
}
// buff[0] === 97
// buff[1] === 98
// buff[2] === 99
// buff[3] === 100
// buff[4] === 101

截取:buf.slice([start[, end]])

用于截取buf,并返回一个新的Buffer实例。需要注意的是,这里返回的Buffer实例,指向的仍然是buf的内存地址,所以对新Buffer实例的修改,也会影响到buf。

var buff1 = Buffer.from('abcde');
console.log(buff1); // <Buffer 61 62 63 64 65> var buff2 = buff1.slice();
console.log(buff2); // <Buffer 61 62 63 64 65> var buff3 = buff1.slice(1, 3);
console.log(buff3); // <Buffer 62 63> buff3[0] = 97; // parseInt(61, 16) ==> 97
console.log(buff1); // <Buffer 61 61 63 64 65>

TODO

  1. 创建、拷贝、截取、转换、查找
  2. buffer、arraybuffer、dataview、typedarray
  3. buffer vs 编码
  4. Buffer.from()、Buffer.alloc()、Buffer.alocUnsafe()
  5. Buffer vs TypedArray

文档摘要

关于buffer内存空间的动态分配

Instances of the Buffer class are similar to arrays of integers but correspond to fixed-sized, raw memory allocations outside the V8 heap. The size of the Buffer is established when it is created and cannot be resized.

相关链接

unicode对照表 https://unicode-table.com/cn/#control-character

字符编码笔记:ASCII,Unicode和UTF-8 http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html

NodeJS学习笔记 (15)二进制数据-buffer(ok)的更多相关文章

  1. NodeJS学习笔记 (29)二进制解码-string_decoder(ok)

    原文:https://github.com/chyingp/nodejs-learning-guide 自己过一遍: 模块简介 string_decoder模块用于将Buffer转成对应的字符串.使用 ...

  2. NodeJS学习笔记 进阶 (1)Nodejs进阶:服务端字符编解码&乱码处理(ok)

    个人总结:这篇文章主要讲解了Nodejs处理服务器乱码及编码的知识,读完这篇文章需要10分钟. 摘选自网络 写在前面 在web服务端开发中,字符的编解码几乎每天都要打交道.编解码一旦处理不当,就会出现 ...

  3. Nodejs学习笔记(六)--- Node.js + Express 构建网站预备知识

    目录 前言 新建express项目并自定义路由规则 如何提取页面中的公共部分? 如何提交表单并接收参数? GET 方式 POST 方式 如何字符串加密? 如何使用session? 如何使用cookie ...

  4. Nodejs学习笔记(六)—Node.js + Express 构建网站预备知识

    前言 前面经过五篇Node.js的学习,基本可以开始动手构建一个网站应用了,先用这一篇了解一些构建网站的知识! 主要是些基础的东西... 如何去创建路由规则.如何去提交表单并接收表单项的值.如何去给密 ...

  5. NodeJS学习笔记 进阶 (13)Nodejs进阶:5分钟入门非对称加密用法

    个人总结:读完这篇文章需要5分钟,这篇文章讲解了Node.js非对称加密算法的实现. 摘录自网络 地址: https://github.com/chyingp/nodejs-learning-guid ...

  6. Nodejs学习笔记(四)——支持Mongodb

    前言:回顾前面零零碎碎写的三篇挂着Nodejs学习笔记的文章,着实有点名不副实,当然,这篇可能还是要继续走着离主线越走越远的路子,从简短的介绍什么是Nodejs,到如何寻找一个可以调试的Nodejs ...

  7. Nodejs学习笔记(十五)--- Node.js + Koa2 构建网站简单示例

    目录 前言 搭建项目及其它准备工作 创建数据库 创建Koa2项目 安装项目其它需要包 清除冗余文件并重新规划项目目录 配置文件 规划示例路由,并新建相关文件 实现数据访问和业务逻辑相关方法 编写mys ...

  8. [转]Nodejs学习笔记(十五)--- Node.js + Koa2 构建网站简单示例

    本文转自:https://www.cnblogs.com/zhongweiv/p/nodejs_koa2_webapp.html 目录 前言 搭建项目及其它准备工作 创建数据库 创建Koa2项目 安装 ...

  9. SQL反模式学习笔记15 分组

    目标:查询得到每组的max(或者min等其他聚合函数)值,并且得到这个行的其他字段 反模式:引用非分组列 单值规则:跟在Select之后的选择列表中的每一列,对于每个分组来说都必须返回且仅返回一直值. ...

随机推荐

  1. Redis-3-string类型

    Redis-3-string类型 标签(空格分隔): redis set key value [ex 秒数] / [px 毫秒数] [nx] /[xx] mset key value key valu ...

  2. 简单的beego分页功能代码

    一个简单的beego分页小插件(源代码在最下面): 支持条件查询 支持参数保留 支持自定义css样式 支持表/视图 支持参数自定义 默认为pno 支持定义生成链接的个数 使用方式: 1)action中 ...

  3. hdu 1257/1800 - 贪心,dp

    1257题目链接 一个序列划分子序列,每个子序列都是非增序列,问最少分成几个子序列 1800题目链接 一堆数分组,每组内数据严格递减,问最少分几组 -------------------------- ...

  4. SpringMVC-注解映射器和适配器_20190323

    1 注解映射器和适配器 1.1 注解映射器spring3.1之前默认加载映射器是org.springframework.web.servlet.mvc.annotation.DefaultAnnota ...

  5. 计算机组成原理--64位CPU装载32位操作系统,它的寻址能力还是4GB吗?

    借由这个问题,今天我们就把 32 位 CPU.64 位 CPU.32 位操作系统.64 位操作系统之间的区别与联系彻底搞清楚.对于这个问题,博主也是一知半解了好长时间啊~ 基本概念 32位的CPU与6 ...

  6. HDU-1052 Tian Ji -- The Horse Racing 贪心 考虑特殊位置(首尾元素)的讨论

    题目链接:https://cn.vjudge.net/problem/HDU-1052 题意 田忌赛马问题扩展版 给n匹马,马的能力可以相同 问得分最大多少 思路 贪心做得还是太少,一开始一点思虑都没 ...

  7. mysql 1067终极解决办法 亲测好使

       进入mysql data 目录 删除 ib_logfile0  ib_logfile1   ibdata1   这三个文件 重启mysql

  8. Python学习————字典的增删改查

    增加:dic1['KEY'] = value -->若之前有KEY,则会覆盖.若没有KEY,则新增至尾处dic.setdefault('KEY',value/None) --->若之前有K ...

  9. 紫书 例题 10-4 UVa 10791(唯一分解定理)

    首先分解,然后可以发现同一个因子ai不能存在于两个以上的数中 因为求的是最小公倍数,如果有的话就可以约掉 所以数字必然由ai的pi次方的乘积组成,那么显然,在 a最小为2,而b大于2的情况下a*b&g ...

  10. 采药 水题 dp 01背包问题 luogu1048

    最基本的01背包,不需要太多解释,刚学dp的同学可以参见dd大牛的背包九讲,直接度娘“背包九讲”即可搜到 #include <cstdio> #include <cstring> ...