nodejs中的Buffer
一,开篇分析
所谓缓冲区Buffer,就是 "临时存贮区" 的意思,是暂时存放输入输出数据的一段内存。
JS语言自身只有字符串数据类型,没有二进制数据类型,因此NodeJS提供了一个与String对等的全局构造函数Buffer来提供对二进制数据的操作。除了可以读取文件得到Buffer的实例外,还能够直接构造,例如:
Buffer与字符串类似,除了可以用.length属性得到字节长度外,还可以用[index]方式读取指定位置的字节,例如:
Buffer与字符串能够互相转化,例如可以使用指定编码将二进制数据转化为字符串:
将字符串转换为指定编码下的二进制数据:
一点儿区别:
Buffer与字符串有一个重要区别。字符串是只读的,并且对字符串的任何修改得到的都是一个新字符串,原字符串保持不变。
至于Buffer,更像是可以做指针操作的C语言数组。例如,可以用[index]方式直接修改某个位置的字节。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
slice方法也不是返回一个新的Buffer,而更像是返回了指向原Buffer中间的某个位置的指针,如下所示。
[ 0x68, 0x65, 0x6c, 0x6c, 0x6f ]
^ ^
| |
bin bin.slice(2)
因此对slice方法返回的Buffer的修改会作用于原Buffer,例如:
var sub = bin.slice(2) ;
sub[0] = 0x65 ;
console.log(buffer) ; // <Buffer 68 65 65 6c 6f>
如果想要拷贝一份Buffer,得首先创建一个新的Buffer,并通过.copy方法把原Buffer中的数据复制过去。
这个类似于申请一块新的内存,并把已有内存中的数据复制过去。以下是一个例子。
var dup = new Buffer(bin.length) ;
buffer.copy(dup) ;
dup[0] = 0x48 ;
console.log(buffer) ; // <Buffer 68 65 6c 6c 6f>
console.log(dup) ; // <Buffer 48 65 65 6c 6f>
总之,Buffer将JS的数据处理能力从字符串扩展到了任意二进制数据。
以上简单让大家了解一下什么是Buffer,下面具体说说如何使用和具体使用场景。
二,聊聊Buffer
JavaScript对字符串处理十分友好,无论是宽字节还是单字节字符串,都被认为是一个字符串。Node中需要处理网络协议、操作数据库、处理图片、文件上传等,还需要处理大量二进制数据,自带的字符串远不能满足这些要求,因此Buffer应运而生。
Buffer结构
Buffer是一个典型的Javascript和C++结合的模块,性能相关部分用C++实现,非性能相关部分用javascript实现。
Node在进程启动时Buffer就已经加装进入内存,并将其放入全局对象,因此无需require
Buffer对象:类似于数组,其元素是16进制的两位数。
Buffer内存分配
Buffer对象的内存分配不是在V8的堆内存中,在Node的C++层面实现内存的申请。
为了高效的使用申请来得内存,Node中采用slab分配机制,slab是一种动态内存管理机制,应用各种*nix操作系统。slab有三种状态:
(1) full:完全分配状态
(2) partial:部分分配状态
(3) empty:没有被分配状态
Buffer的转换
Buffer对象可以和字符串相互转换,支持的编码类型如下:
ASCII、UTF-8、UTF-16LE/UCS-2、Base64、Binary、Hex
字符串转Buffer
new Buffer(str, [encoding]),默认UTF-8
buf.write(string, [offset], [length], [encoding])
Buffer转字符串
buf.toString([encoding], [start], [end])
Buffer不支持的编码类型
通过Buffer.isEncoding(encoding)判断是否支持
iconv-lite:纯JavaScript实现,更轻量,性能更好无需C++到javascript的转换
iconv:调用C++的libiconv库完成
Buffer的拼接
注意 "res.on('data', function(chunk) {})",其中的参数chunk是Buffer对象,直接用+拼接会自动转换为字符串,对于宽字节字符可能会导致乱码产生,
解决方法:
(1) 通过可读流中的setEncoding()方法,该方法可以让data事件传递不再是Buffer对象,而是编码后的字符串,其内部使用了StringEncoder模块。
(2) 将Buffer对象暂存到数组中,最后在组装成一个大Buffer让后编码转换为字符串输出。
Buffer在文件I/O和网络I/O中广泛应用,其性能举足轻重,比普通字符串性能要高出很多。
Buffer的使用除了与字符串的转换有性能损耗外,在文件读取时候,有一个highWaterMark设置对性能影响至关重要。
a,highWaterMark设置对Buffer内存的分配和使用有一定影响。
b, highWaterMark设置过小,可能导致系统调用次数过多。
什么时候该用buffer,什么时候不该用 ------ 纯粹的javascript支持unicode码而对二进制不是很支持,当解决TCP流或者文件流的时候,处理流是有必要的,我们保存非utf-8字符串,2进制等等其他格式的时候,我们就必须得使用 ”Buffer“ 。
三,实例引入
console.time("buffer concat test !");
var list = [] ;
var len = 100000 * buf.length ;
for(var i=0;i<100000;i++){
list.push(buf) ;
len += buf.length ;
}
var s1 = Buffer.concat(list, len).toString() ;
console.timeEnd("buffer concat test !") ;
console.time("string concat test !") ;
var list = [] ;
for (var i = 100000; i >= 0; i--) {
list.push(str) ;
}
var s2 = list.join("") ;
console.timeEnd("string concat test !") ;
以下是运行结果:

读取速度肯定string更快,buffer还需要toString()的操作。 所以我们在保存字符串的时候,该用string还是要用string,就算大字符串拼接string的速度也不会比buffer慢。
那什么时候我们又需要用buffer呢?没办法的时候,当我们保存非utf-8字符串,2进制等等其他格式的时候,我们就必须得使用了。
四,总结一下
(1),JavaScript适合处理Unicode编码数据,但对二进制数据的处理并不友好。
(2),所以处理TCP流或文件系统时,对八位字节流的处理很有必要。
(3),Node有几个用于处理,创建和消耗八位字节流的方法。
(4),原始数据存放在一个Buffer实例中,一个Buffer类似一个整数数组,但是它的内存,分配在V8堆栈外。一个Buffer的大小是不能更改的。
(5),处理的编码类型有:ascii,utf8,utf16le,ucs2(utf16le的别名),base64,binary,hex。
(6),Buffer为全局元素,直接new Buffer()就得到一个Buffer实例。
nodejs中的Buffer的更多相关文章
- 理解 nodeJS 中的 buffer,stream
在Node.js开发中,当遇到 buffer,stream,和二进制数据处理时,你是否像我一样,总是感到困惑?这种感觉是否会让你认为不了解它们,以为它们不适合你,认为而这些是Node.js作者们的事情 ...
- nodejs中Stream的理解
在nodejs中可以通过fs模块读写文件,我们来看下fs模块提供的接口: fs.readFile(filename, callback) 异步读取文件. filename是读取文件的文件名,如果是相对 ...
- nodeJS中读写文件方法的区别
导言:nodejs中所有与文件相关的操作都在fs模块中,而读写操作又是我们会经常用到的操作,nodejs的fs模块针对读操作为我们提供了readFile,read, createReadStream三 ...
- nodeJS之二进制buffer对象
前面的话 在ES6引入TypedArray之前,JavaScript语言没有读取或操作二进制数据流的机制.Buffer类被引入作为Nodejs的API的一部分,使其可以在TCP流和文件系统操作等场景中 ...
- nodejs中http-proxy使用小结
最近在写xmocker的工具,用于开发前期的mock数据,不可避免的用到了代理的中间件.当然,github上有关于http-proxy封装的中间件.毕竟是自己练手的项目,就自己写了个中间件,方便定制功 ...
- 虹软SDK在nodejs中的集成
==虹软官网地址== http://www.arcsoft.com.cn 在官网注册账号,并且申请人脸识别激活码, 选择SDK版本和运行系统(windows/linux/android/ios) ,我 ...
- 在nodejs中的集成虹软人脸识别
==虹软官网地址==http://www.arcsoft.com.cn 在官网注册账号,并且申请人脸识别激活码, 选择SDK版本和运行系统(windows/linux/android/ios) ,我们 ...
- nodejs中mysql用法
nodejs也算是一篇脚本了我们来看nodejs如何使用mysql数据库了有了它们两组合感觉还是非常的不错哦,下面一起来看nodejs中使用mysql数据库的示例,希望能够帮助到各位. <scr ...
- nodejs中aes-128-cbc加密和解密
和java程序进行交互的时候,java那边使用AES 128位填充模式:AES/CBC/PKCS5Padding加密方法,在nodejs中采用对应的aes-128-cbc加密方法就能对应上,因为有使用 ...
随机推荐
- Telegraf+InfluxDB+Grafana搭建服务器监控平台
Telegraf+InfluxDB+Grafana搭建服务器监控平台 tags:网站 个人网站:https://wanghualong.cn/ 效果展示 本站服务器状态监控:https://statu ...
- 学习string,stringBuffer时遇到的问题
今天学习string和stringBuffer.了解了两者的区别,然后去看java api都有啥方法.stringBuffer类有indexOf方法,于是写了下面的代码 String str = &q ...
- sql server 删除所有表和递归查询、数字类型转为字符串
1.删除所有表 select 'drop table '+name+';' from sys.tables where name like 'DataSyncV1DelaySample%' or na ...
- eclipse出现jdk版本更新导致无法启动
启动出现的问题,截图: 解决办法: 1.找到自己jdk安装的bin目录,我的安装目录是:F:\jdk\bin 2.修改eclipse安装目录下的eclipse.ini,添加 -vmF:\jdk\bin ...
- RE模块垃圾回收机制
今日内容 一.垃圾回收机制 函义:不能被程序访问到的数据,就称之为垃圾 1.引用计数 引用计数是用来记录值的内存地址被记录的次数的 每一次对值地址的引用都可以使该值的引用计数 +1 每一次对值地址的 ...
- 关于lamp环境搭建过程的教程
一.搭建lamp 的网址 https://lamp.sh/install.html 二.对于linux下上传图片或音频失败原因? 1.必须将文件夹的权限设置为apache 命令为:chown -R a ...
- C# - 学习总目录
C# - 基础 C# - 操作符 C# - 值类型和引用类型 C# - 表达式与语句 C# - 数组 C# - 引用类型 C# - 常用类 C# - 常用接口 C# - LINQ 语言集成查询 C# ...
- spring boot 业务场景简单,代码完整的demo们
源码地址:https://github.com/zhzhair/spring-boot-examples.git 开发环境:windows,jdk8,spring boot2.1.4
- [Kubernetes]基于角色的权限控制之RBAC
Kubernetes中有很多种内置的编排对象,此外还可以自定义API资源类型和控制器的编写方式.那么,我能不能自己写一个编排对象呢?答案是肯定的.而这,也正是Kubernetes项目最具吸引力的地方. ...
- 在JavaScript中,如何判断数组是数组?
如果你没有注意过这个问题,那么这个标题应该会让你感到困惑,判断数据类型这么基础的问题能有什么坑呢? 少年,你不能太天真了,我们朝夕面对的这门语言,可是JavaScript呀,任何你觉得已经习以为常的东 ...