详解nodejs 文本操作模块-fs模块(二)


Posted in NodeJs onDecember 22, 2016

前一篇学习了文件的打开和关闭,文件操作总不能只包含打开和关闭吧,这里就开始文件的读写操作。

fs模块方法

1:read和readSync方法

该方法,是从文件的指定位置处读取文件,一直读取到文件底部,然后江都区到的内容输出到一个缓存区,使用方法如下:

fs.read(fd,buffer,offset,length,position,callback);

在read方法中,支持6个参数:

  • fd参数,是文件描述符,是open方法的回调函数中获取到的,是一个数字。
  • buffer,是一个buffer对象,用于指定将文件数据读取到那个缓存区,如果不定义,则会生成一个新的缓存区,进行存放新读取到的数据。
  • offset,是一个整数值,用于指定向缓存区中写入数据时的开始位置,以字节为单位。其实也就是,读入到缓存中的数据,从buffer对象的第几个元素开始写入。
  • length,是一个整数值,表示读入的数据,多少数据写入到buffer对象中去,要保证不能超出buffer的容纳范围,否则会抛出一个范围异常。
  • position,是一个整数值,表示,从文件中的哪个位置,开始读取数据,如果设置为非0的整数,则从该整数所示的位置,读取长度为length的数据到buffer对象中。
  • callback,回调函数,当读取文件成功之后,把执行该函数,该回调函数支持三个参数:
function (err,bytesRead,buffer){ 
 //err为读取文件操作失败时,触发的错误对象 
 //bytesRead为读取到的字节数,如果文件的比较大,则该值就是length的值, 
 //如果文件的大小比length小,则该值为实际中读取到的字节数。 
 //buffer为读取到的内容,保存到了该缓存区,如果在使用read时, 
 //传入了buffer对象,则此处的buffer就是传入的buffer对象。 
 //如果在read时没有传入buffer,则此处的buffer为新创建的buffer对象 
}

上面把参数的含义以及回调函数的定义,都说明了一下,这里就看一个示例吧:

var fs = require("fs"); 
 
fs.open("fs.txt","r",function(err,fd){ 
 //读取fs.text,文件的内容为“123456789”,长度为9 
 var buffer = new Buffer([0,0,0,0,0,0,0,0,0,0]); 
 //创建一个长度为10,初始值为0的buffer对象。 
 //数据比较少,就直接写了,否则还是使用fill方法吧。 
 console.log(buffer); 
 //<Buffer 00 00 00 00 00 00 00 00 00 00> 
 //初始时的buffer对象 
  
 fs.read(fd,buffer,4,6,4,function(err,bytesRead,buffer1){ 
  //读取到的数据,从buffer对象的第5个元素开始保存,保存6个字节的元素 
  //读取文件,是从文件的第5个字节开始,因为文件中内容长度为9, 
  //那么,读取到的内容就是56789,所以buffer的最后一位仍然为初始值。 
  //由于想要读取的字节长度为6,但是文件内容过短,只读取了5个字节的有效数据 
  //就到了文件的结尾了,所以,bytesRead的值不是6,而是5。 
  //而buffer对象,为被写入新数据之后的对象。 
  console.log(bytesRead); //5 
    console.log(buffer1);  
  //<Buffer 00 00 00 00 35 36 37 38 39 00> 
    console.log(buffer); 
  //<Buffer 00 00 00 00 35 36 37 38 39 00> 
  //它们俩是完全相同的。其实质是,它们俩占据的内存也是相同的, 
  //它们就是同一个缓存区。 
 }); 
});

一般情况下,异步调用时,回调函数中,只有两个参数存在,第一个参数为err对象,第二个参数为操作之后的数据,可是,这里有三个数据,那么在同步时,什么才是返回值呢?

所以,要做如下的测试:

var fs = require("fs"); 
 
fs.open("fs.txt","r",function(err,fd){ 
 //读取fs.text,文件的内容为“123456789”,长度为9 
 var buffer = new Buffer([0,0,0,0,0,0,0,0,0,0]); 
  
 var bytesRead = fs.readSync(fd,buffer,4,6,4); 
 console.log(bytesRead); 
});

返回的是bytesRead的值,并没有返回buffer对象,可以想象,因为buffer对象是原本传入的buffer对象,依然可以通过传入的buffer对象,直接访问到重写数据之后的buffer对象。

但是,有个问题就来了,如果没有传入buffer对象呢?这又要如何呢?这个问题暂且别过,因为这个问题,并没有在一些API文档中说明,在书中也没有看到这个用法,但是接下来,我们去分析一下源码,就能发现,除了上述的两种常用的方法之外,还有其他的使用方式。

OK,先看下read方法的源码:

fs.read = function(fd, buffer, offset, length, position, callback) { 
 if (!util.isBuffer(buffer)) { 
 //如果传入的第二个参数不是一个buffer对象,则做一些自适应的处理 
 // legacy string interface (fd, length, position, encoding, callback) 
 var cb = arguments[4], 
  encoding = arguments[3]; 
 //本来read方法是有6个参数的,当buffer没有传入的时候, 
 //则相应的offset也变得没有意义,所以变为了4个参数。 
 //而这个时候,参数的形式就变成了前面英文部分的样子。5个参数,加入了encoding参数。 
  
 assertEncoding(encoding); 
 //判断传入的encoding是否是当前支持的编码方式 
 //如果不是,则抛出异常 
 
 position = arguments[2]; 
 length = arguments[1]; 
 buffer = new Buffer(length); 
 offset = 0; 
 //设置对应的值,新建buffer对象 
 
 //把callback做一个代理,根据传入的编码方式,把结果按照指定的编码,传入回调函数 
 callback = function(err, bytesRead) { 
  if (!cb) return; 
  //如果回调函数不存在,则直接退出 
  
  var str = (bytesRead > 0) ? buffer.toString(encoding, 0, bytesRead) : ''; 
 
  //注意,当读取文件成功后,执行了wrapper的回调,从wrapper中, 
  //执行到该callback回调时,并没有传入buffer对象, 
  //并且,调用read中的回调的三个参数是:err,str(按照指定编码之后的字符串), 
  //bytesRead(读取字节数),并没有buffer对象传入 
  (cb)(err, str, bytesRead); 
 }; 
 } 
 
 function wrapper(err, bytesRead) { 
 // Retain a reference to buffer so that it can't be GC'ed too soon. 
 // 由这里可以看出,在C++读取文件时,回调函数只有两个值 
 //err对象和真实读取的字节数,至于buffer对象,则是nodejs代理之后 
 //给添加上的 
 callback && callback(err, bytesRead || 0, buffer); 
 } 
 
 //创建一个实例,定义oncomplete属性 
 //该实例,按照猜测,应该是分段读取文件的一个对象 
 //当读取文件完成之后,会执行oncomplete方法 
 var req = new FSReqWrap(); 
 req.oncomplete = wrapper; 
 
 //调用C++的接口,开始读取文件 
 binding.read(fd, buffer, offset, length, position, req); 
};

看了上面的源码分析,那么也就发现了另外一种使用read的方法了,即,不输入buffer和offset,添加encoding的5个参数的使用,举一个最简单的实例吧。

var fs = require("fs"); 
 
fs.open("fs.txt","r",function(err,fd){ 
 //读取fs.text,文件的内容为“123456789”,长度为9 
 var buf1 = new Buffer([0,0,0,0,0,0,0,0,0,0]); 
  
 fs.read(fd,6,4,null,function(err,str,bytesRead){ 
  console.log(err); 
  //null 
  console.log("str="+str); 
  //str=56789 
  console.log("bytesRead="+bytesRead); 
  //bytesRead=5 
 }); 
  
});

注意,当不传入buffer对象时,回调函数中的三个参数也相应的有了变化,详情请看前面的实例代码中,回调函数的参数以及源码中的注释。

继续看下readSync的源码,在本文的前面,也给出了一个readSync的示例,当传入buffer对象时,返回值是读取到真是字节数,那么,既然read方法可以省略buffer对象,改为返回读取到的字符串,那么readSync方法呢?这个就让我们看下源码中,是如何处理这些数据的。

fs.readSync = function(fd, buffer, offset, length, position) { 
 var legacy = false; 
 if (!util.isBuffer(buffer)) { 
 // legacy string interface (fd, length, position, encoding, callback) 
 //该部分的处理,和read方法内部,完全相同,不再注释。 
 //唯一区别,legacy标识符,标志是否传入了buffer,为false时,表示传入了 
 legacy = true; 
 var encoding = arguments[3]; 
 
 assertEncoding(encoding); 
 
 position = arguments[2]; 
 length = arguments[1]; 
 buffer = new Buffer(length); 
 
 offset = 0; 
 } 
 
 //C++的read方法,如果传入了第六个参数,则属于读取成功之后,执行的回调相关的对象 
 //如果不传入,则返回值为读取到的真是字节数,该数小于等于length 
 var r = binding.read(fd, buffer, offset, length, position); 
 if (!legacy) { 
 //如果,传入了buffer对象,则直接返回读取到的真是字节数 
 return r; 
 } 
 
 var str = (r > 0) ? buffer.toString(encoding, 0, r) : ''; 
 //如果没有传入buffer对象,那么返回一个数组,该数组包含两个元素, 
 //字符串和读取到的字节数 
 return [str, r]; 
};

那么接下来看下,如果不传入buffer对象时的一个示例吧:

var fs = require("fs"); 
 
fs.open("fs.txt","r",function(err,fd){ 
 //读取fs.text,文件的内容为“123456789”,长度为9 
 var buf1 = new Buffer([0,0,0,0,0,0,0,0,0,0]); 
  
 var arr = fs.readSync(fd,6,4,null); 
 console.log(arr); 
 //["56789",5] 
});

OK,到这里,关于read和readSync方法的使用及一些原理性东西,也基本说完了。

2:write和writeSync方法

有读取的方法,那么就必然有写入的方法了,要么flag=w不就无用了么。并且看到了前面的关于read的一些使用,那么接下来,对于write的使用,看起来就变得更加的简单了,现在直接看下示例:

var fs = require("fs"); 
 
fs.open("fs.txt","a+",function(err,fd){ 
 //读取fs.text,文件的内容为“123456789”,长度为9 
 var buf1 = new Buffer("我喜爱Nodejs"); 
 console.log(buf1); 
 //显示buf1的buffer数据 
 //计算buf1的长度,把该数据全部写入到fs.txt文件中 
 fs.write(fd,buf1,0,buf1.length,0,function(err,len,buf){ 
  console.log("len="+len); 
  //写入的长度 
   
  //写入的buf,其实和buf1完全相等 
  console.log(buf); 
  fs.read(fd,len,9,"utf8",function(err,str,len2){ 
   console.log("len2="+len2); 
   //读取从9开始的数据 
   console.log("str="+str); 
   //读取相应得到的字符串 
   //我喜爱Nodejs 
  }); 
 }); 
});

从上面这个示例可以看出,write方法和read方法,使用基本是完全一样的,只是一个是在读取文件一个是在写入文件,前提也是需要你在open打开文件时,使用的flag打开文件方式,要支持读写才行。

既然,write和read是相同的使用方法,那么也可以不定义buffer的直接写入数据,所以,可以继续看下面的这个示例:

var fs = require("fs"); 
 
fs.open("fs.txt","a+",function(err,fd){ 
 //读取fs.text,文件的内容为“123456789”,长度为9 
  
 //复杂的写法,和简单的写法,就看个人喜好了,0代表的是字符串的开始位置 
 //fs.write(fd,"我喜爱Nodejs",0,"utf8",function(err,len,str) 
 fs.write(fd,"我喜爱Nodejs",function(err,len,str){ 
  console.log("len="+len); //len=15 
  //写入的长度 
   
  //当直接写入字符串时,返回的也不再是buffer对象,而是字符串 
  console.log("str="+str); //我喜爱Nodejs 
  fs.read(fd,len,9,"utf8",function(err,str,len2){ 
   console.log("len2="+len2); //len2=15 
   //读取从9开始的数据 
   console.log("str="+str); 
   //读取相应得到的字符串 
   //我喜爱Nodejs 
  }); 
 }); 
});

这里就不再分析源码了,基本上write的源码和read的源码处理方式类似,只是在最后调用C++接口不同而已,所以这里也就不再占用空间了。有兴趣的可以直接去nodejs的github源码中,查看:fs.js。

关于writeSync的用法,用法和write是相同的,只是不需要回调函数,并且也不需要返回写入的数据,所以,和readSync的区别,也就是,readSync在不传入buffer时,会返回一个长度为2的数组,而writeSync不受buffer对象的影响,只要写入成功,就会返回写入的真实字节数。
不加示例,不加源码分析,请参考上面的read方法,readSync方法和write方法,也可以参考nodejs的API文档:Nodejs的API中文版。

总结

本篇的read和write是文档操作的基础,是属于最基本的操作,也是最重要的操作,本篇也是属于fs模块中的基本使用方法,对于以后学习其他方法,以及更好的了解fs模块有重要的作用,好好学习,天天向上。

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

NodeJs 相关文章推荐
nodejs 后缀名判断限制代码
Mar 31 NodeJs
NodeJS Express框架中处理404页面一个方式
May 28 NodeJs
Nodejs+express+html5 实现拖拽上传
Aug 08 NodeJs
nodejs实现获取某宝商品分类
May 28 NodeJs
nodejs爬虫抓取数据之编码问题
Jul 03 NodeJs
NodeJs的优势和适合开发的程序
Aug 14 NodeJs
详解nodejs微信公众号开发——4.自动回复各种消息
Apr 11 NodeJs
在Debian(Raspberry Pi)树莓派上安装NodeJS的教程详解
Sep 19 NodeJs
NVM安装nodejs的方法实用步骤
Jan 16 NodeJs
nodejs log4js 使用详解
May 31 NodeJs
NodeJs实现简易WEB上传下载服务器
Aug 10 NodeJs
nodejs实现的http、https 请求封装操作示例
Feb 06 NodeJs
学习 NodeJS 第八天:Socket 通讯实例
Dec 21 #NodeJs
详解Nodejs基于mongoose模块的增删改查的操作
Dec 21 #NodeJs
nodejs redis 发布订阅机制封装实现方法及实例代码
Dec 15 #NodeJs
解析NodeJs的调试方法
Dec 11 #NodeJs
nodejs连接mongodb数据库实现增删改查
Dec 01 #NodeJs
Nodejs 搭建简单的Web服务器详解及实例
Nov 30 #NodeJs
Nodejs下用submit提交表单提示cannot post错误的解决方法
Nov 21 #NodeJs
You might like
PHP的面试题集
2006/11/19 PHP
超强分页类2.0发布,支持自定义风格,默认4种显示模式
2007/01/02 PHP
PHP最常用的ini函数分析 针对PHP.ini配置文件
2010/04/22 PHP
php输出指定时间以前时间格式的方法
2015/03/21 PHP
使用GruntJS链接与压缩多个JavaScript文件过程详解
2013/08/02 Javascript
对 jQuery 中 data 方法的误解分析
2014/06/18 Javascript
jQuery中[attribute*=value]选择器用法实例
2014/12/31 Javascript
基于jQuery Bar Indicator 插件实现进度条展示效果
2015/09/30 Javascript
学习Angularjs分页指令
2016/07/01 Javascript
响应式表格之固定表头的简单实现
2016/08/26 Javascript
chrome下判断点击input上标签还是其余标签的实现方法
2016/09/18 Javascript
Node.js的环境安装配置(使用nvm方式)
2016/10/11 Javascript
浅谈javascript alert和confirm的美化
2016/12/15 Javascript
JavaScript中数组的各种操作的总结(必看篇)
2017/02/13 Javascript
关于vue-resource报错450的解决方案
2017/07/24 Javascript
浅谈JS对象添加getter与setter的5种方法
2018/06/09 Javascript
解决webpack+Vue引入iView找不到字体文件的问题
2018/09/28 Javascript
微信小程序实现保存图片到相册功能
2018/11/30 Javascript
IDEA配置jQuery, $符号不再显示黄色波浪线的问题
2020/10/09 jQuery
python处理图片之PIL模块简单使用方法
2015/05/11 Python
Python使用reportlab将目录下所有的文本文件打印成pdf的方法
2015/05/20 Python
在Python中执行系统命令的方法示例详解
2017/09/14 Python
python3之模块psutil系统性能信息使用
2018/05/30 Python
啥是佩奇?使用Python自动绘画小猪佩奇的代码实例
2019/02/20 Python
python实现图片转字符小工具
2019/04/30 Python
Python分割训练集和测试集的方法示例
2019/09/19 Python
Flask项目中实现短信验证码和邮箱验证码功能
2019/12/05 Python
PyTorch笔记之scatter()函数的使用
2020/02/12 Python
如何基于线程池提升request模块效率
2020/04/18 Python
Python基于yaml文件配置logging日志过程解析
2020/06/23 Python
matplotlib绘制多子图共享鼠标光标的方法示例
2021/01/08 Python
Ellesse英国官网:意大利高级运动品牌
2019/07/23 全球购物
伦敦最受欢迎的蛋糕店:Konditor & Cook
2019/11/01 全球购物
英国女性化妆品收纳和家具网站:Beautify
2019/12/07 全球购物
售后服务承诺书
2014/03/26 职场文书
Python中seaborn库之countplot的数据可视化使用
2021/06/11 Python