在node咱们若是操做文件,就须要读取buffer.经过fs读取javascript
文件操做,流的操做都要用到buffer,java
一个字节最大多少? 假设8个1用10进制表示 265,他们分别有一下几个特色:node
2进制 | 8进制 | 10进制 | 16进制 |
---|---|---|---|
0b开头 | 0o开头 | 278 | 0x开头 |
(278).tostring(16)
=> 116好比 unicode编码都对应着一个utf8
规则linux
十六进制 | utf8编码方式 |
---|---|
0000 0000-0000 007f | 0xxxxxxx |
0000 0080-0000 07ff | 110xxxxx 10xxxxxx |
0000 0800-0000 ffff | 1110xxxx 10xxxxxx 10xxxxxx |
0001 0000-0000 ffff | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
若是是汉字,是3个字节,那就是第三行。对于英语字母,是两个字节,和 ASCLL是相同的,咱们区分他们用的就是编码前面的标识。npm
好比咱们要转换一个 16进制编码,代码以下数组
function transfer(r){
let code = [1110,10,10]; //汉字三个字节,拼接字符串
code[2] += (r).toString(2).slice(-6);
code[1] += (r).toString(2).slice(-12,-6);
code[0] += ((r).toString(2).slice(0,-12)).padStart(4,0);
code = code.map((item)=>parseInt(item,2)) ; //将二进制转换回十进制
return Buffer.from(code).toString() //转换成buffer在转换成汉字
}
复制代码
node要操做文件都是二进制,声明一段内存,有两个方法:buffer.from
,buffer.alloc
;promise
buffer将二进制转换为16进制展现,非分配内存的大小不能更改,好比v8 1.7g(64) 0.9g(32),内存是属于非引用空间,可是属于引用类型,很是像二维数组,buffer.alloc
会输出 <Buffer 00 00 00 00 00 00>
,安全,速度慢,经过长度建立,buffer.from
经过十进制的数组或者字符串,node不支持gb2312 格式,可是当咱们爬虫一个这种类型的网站的时候,能够用一个包(icon-lite),使用以前须要安装,使用方法,请参考www.npmjs.com/package/ico…缓存
let fs = require('fs');
let path = require('path');
let iconvlite = require('iconv-lite'); //这个包须要转化的是buffer
let r = fs.readFileSync(path.resolve(__dirname,'a.txt'));
let result = iconvlite.decode(r,'gbk')//能够转换成任意格式
console.log(result)
复制代码
因为buffer其实是二进制,那么他就能够用toString
进行转换安全
let buffer = Buffer.from('走开')
console.log(buffer.slice(0,2).toString())
console.log(buffer.slice(2,6).toString())
=> �
�开
复制代码
上面这种状况如何解决呢?也是一个内置方法异步
let buffer = Buffer.from('走开')
let a = buffer.slice(0,2);
let b = buffer.slice(2,6);
let { StringDecoder } = require('string_decoder');
let sd = new StringDecoder();
console.log(sd.write(a)) //此时不是正常汉字则保留在sd的内部
console.log(sd.write(b)) //下次输出会把上次的结果一同输出
=> 走开
复制代码
缓存输出,把不能拼成汉字的先缓存,在输出.
buffer的其余方法buffer.copy
,buffer.concat
,具体实现
let buffer1 = Buffer.alloc(6);
let buffer2 = Buffer.from("走开");
Buffer.prototype.mycopy = function(target,targetStart,sourceStart,sourceEnd){
for( let i = 0;i< sourceEnd - sourceStart;i++){
target[i + sourceStart] = this[sourceStart + i]
}
}
buffer2.mycopy(buffer1,1,3,6);//目标buffer, 目标开始的拷贝位置,源的开始和结束位置
console.log(buffer1.toString());
buffer2.copy(buffer1,1,3,6);
console.log(buffer1.toString());
复制代码
concat
方法进行拼接let buffer1 = Buffer.from("走");
let buffer2 = Buffer.from("开");
let buffer3 = Buffer.from("啊");
Buffer.concat([buffer1,buffer2,buffer3]);//目标buffer, 目标开始的拷贝位置,源的开始和结束位置
console.log(Buffer.concat([buffer1,buffer2,buffer3]).toString());
Buffer.myconcat = function(list,len){
//计算要生成的buffer长度,将list每一项求和
if(typeof len === 'undefined'){
len = list.reduce((current,next,index) => {
return current + next.length
},0)
}
let newBuffer = Buffer.alloc(len);
let index = 0;
list.forEach(buffer => {
buffer.copy(newBuffer,index)
index += buffer.length;
});
return newBuffer.slice(0,index)
}
Buffer.myconcat([buffer1,buffer2,buffer3]);//目标buffer, 目标开始的拷贝位置,源的开始和结束位置
console.log(Buffer.myconcat([buffer1,buffer2,buffer3]).toString());
复制代码
相似还有indexof,下面咱们在原型上扩展一个split方法
let buffer1 = Buffer.from("走**走**走");
Buffer.prototype.split = function(sep){
let index = 0;
let len = Buffer.from(sep).length;//查找buffer的长度
let i = 0;
let arr = []
while( -1 != (i = this.indexOf(sep,index))){
let a = this.slice(index,i);
index = i + len;
arr.push(a)
}
arr.push(this.slice(index))
return arr.map(item => item.toString()));
}
buffer1.split('**');
复制代码
fs模块, 在nodejs中,使用fs模块来实现全部有关文件及目录建立,写入删除操做,全部方法氛围同步异步两种实现,带sync为同步,反之异步,在此以前咱们先知道权限的问题
文件类型与权限 | 连接占用的节点 | 文件全部者 | 文件全部者的用户组 | 文件大小 | 文件建立的事件 | 最近修改时间 | 文件名称 |
---|---|---|---|---|---|---|---|
-rw-r--r-- | 1 | root | root | 34298 | 04-02 | 00:23 | install.log |
-
表明当前是文件,
d
表明目录,
r
表明读
w
表明写,`````
权限项 | 读 | 写 | 执行 | 读 | 写 | 执行 | 读 | 写 | 执行 |
---|---|---|---|---|---|---|---|---|---|
字符表示 | r | w | x | r | w | x | r | w | x |
数字表示 | 4 | 2 | 1 | 4 | 2 | 1 | 4 | 2 | 1 |
权限分配 | 文件全部者 | 文件全部组 | 其余用户 |
他们三个组成权限位:二爷一直死读书
//文件中存的永远是二进制
let fs = require('fs');
fs.readFile('1.txt',{encoding:'utf8',flag:'r'},function(err,data){
if(err) return console.log(err);
console.log(data);
})
//写
fs.writeFile('a.txt',Buffer.from('123'),{flag:'',mode:0o444},function(err,data){
if(err) return console.log(err);
console.log("写入成功");
})
//copy
fs.copyFile('a.txt','3.txt',function(err,data){
console.log("拷贝成功");
})
复制代码
flag
let fs = require('fs');
//fd文件描述符,符号从3开始, 0 标准输入,1标准输出 2表明错误输出
fs.open('1.txt','r',function(err,fd){
//把文件中的内容读取到buffer中,offsetbuffer是buffer的偏移量
let BFFER_SIZE = 3;
let buffer = Buffer.alloc(3);//读取到哪一个buffer上
//将fd的内容读到buffer里,从第0开始读,读BFFER_SIZE个,从文件的第0 个位置开始读,成功之后回调,byteRead实际读到的个数
// fs.read(fd,buffer,0,BFFER_SIZE,0,function(err,byteRead){
// fs.read(fd,buffer,0,BFFER_SIZE,0,function(err,byteRead){
// fs.read(fd,buffer,0,BFFER_SIZE,0,function(err,byteRead){
// })
// })
// })
//等同于
let index = 0;
function next(){
fs.read(fd,buffer,0,BFFER_SIZE,index,function(err,byteRead){
index += byteRead;
if(byteRead == BFFER_SIZE){
next()
}else{
fs.close(fd,()=>{
console.log('close')
})
}
console.log(buffer.slice(0,byteRead).toString())
})
};
next();
})
复制代码
let fs = require('fs');
function copy(source,target){
let index = 0;
let buffer = Buffer.alloc(3);
let BUFFER_SIZE = 3;
fs.open(source,'r',function(err,rfd){//开启读取文件描述符
if(err) return console.log(err);
fs.open(target,'w',0o666,function(err,wfd){ // 开启写入描述符
function next(){
fs.read(rfd,buffer,0,BUFFER_SIZE,index,function(err,byteRead){
fs.write(wfd,buffer,0,byteRead,index,function(err,byteWritten){
index += byteRead;
if(byteWritten){//若是有写入内容,就继续读取
next();
}else{
fs.close(rfd,()=>{});
//吧内存中的内容强制写入在关闭文件,觉得写入是异步操做
fs.fsync(function(){
fs.close(wfd,()=>{});
})
}
})
})
}
next();
})
})
}
copy('5.txt','6.txt')
复制代码
若是你报错binding.fsync(fd, req);
^
TypeError: fd must be a file descriptor
fs.write这个方法在执行的时候会检测该文件是否已经存在了,若是已经存在便会报这个错误,解决办法就是先删掉本地该文件,而后再执行就成功了,以下所示。 详情请参考https://blog.csdn.net/u012453843/article/details/60958779
固然以上这么麻烦,因而咱们有流,流提供了copy的功能,pipe 有关流清参考文章
fs主要作fileSystem,咱们能够经过他监听文件的变化,判断文件的状态,文件的读写还有目录的操做等等
let fs = require('fs');
//建立目录,同步方法和异步方法,若是只读一次建议使用同步,同步编写比较容易
//异步不会阻塞主线程,性能高一些
//fs.mkdirSync('a/b') //只能建立目录,不能建立js文件,不能跨级建立,必须保证父级存在
function makep(dir,callback){
let dirs = dir.split('/');
let index = 1;
function next(index){
//当索引溢出时 就不要递归了
if(index === dir.length + 1 ) return callback;
let p = dirs.slice(0,index).join('/');
fs.access(p,(err) => {
if(!err){
next(index+1);
}else{
//若是没有这个文件就会走到err中,建立这个文件,建立完毕后建立下一个文件
fs.mkdirp(p,(err) => {
if(err) return console.log(err);
next(index + 1)
})
}
})
}
next(index)
}
makep('a/b/c/d')
复制代码
当咱们删除文件夹的时候fs.rmdirSync('a');
有时候会报错误directory not empty
,也就是说删除文件夹必须保证a目录是空的,此时咱们须要对文件夹进行遍历, 遍历分先序,中序,后序,(删除目录通常是先序)或者深度和广度 ;
先写一个同步一层的
let fs = require('fs');
let path = require('path');
//只能读儿子目录
let dirs = fs.readdirSync('c');
dirs = dirs.map(item => path.join('c',item))
dirs.forEach(p =>{
let stat = fs.statSync(p);
console.log('atat' + stat.isDirectory())
if(stat.isDirectory()){//是不是文件夹
fs.rmdirSync(p)//删除目录
}else{
fs.unlinkSync(p) //删除文件
}
})
fs.rmdirSync('c')//删除本身
复制代码
多层删除,先序深度
let fs = require('fs');
let path = require('path');
//同步删除文件夹
function removeDirSync(dir){
//容许人家删除的不必定是目录
let stat = fs.statSync(dir);
if(stat.isDirectory()){//是不是文件夹
let dirs = fs.readdirSync(dir);
dirs = dirs.map(item => path.join(dir,item))
dirs.forEach(d =>{
removeDirSync(d)
})
fs.rmdirSync(dir)//最后删除本身
}else{
fs.unlinkSync(dir) //删除文件
}
}
removeDirSync('c')
复制代码
异步删除文件夹
let fs = require('fs');
let path = require('path');
//异步删除文件夹 promise
function removeDir(dir){
return new Promise((resolve,reject) => {
fs.stat(dir,(err,stat)=> {//第二个参数返回的是以前let stat
if(stat.isDirectory()){//是不是文件夹
let dirs = fs.readdir(dir,(err,dirs)=>{
dirs = dirs.map(item => path.join(dir,item));
dirs = dirs.map(p => removeDir(p)); //保证返回的是promise
//当两个方法同时完成的时候
Promise.all(dirs).then(()=>{
fs.rmdir(dir,resolve) //删除本身
})
});
}else{
fs.unlink(dir, resolve) //删除文件
}
})
})
}
removeDir('c').then(data =>{
console.log('成功')
})
复制代码
//异步删除文件夹
let fs = require('fs');
let path = require('path');
//异步删除文件夹 promise
function rmdir(dir,callback){
fs.stat(dir,(err,stat)=> {//第二个参数返回的是以前let stat
if(stat.isDirectory()){//是不是文件夹
let dirs = fs.readdir(dir,(err,dirs)=>{
//只要涉及到异步递归就用next
function next(index){
if(dirs.length === 0 ||(index == dirs.length)) {
return fs.rmdir(dir,callback)
}
let p = path.join(dir,dirs[index]);
rmdir(p,() => next(index+1));
}
next(0);
});
}else{
fs.unlink(dir,callback) //删除文件
}
})
}
rmdir('a',() =>{
console.log('delecte ok')
})
复制代码
广度删除
let fs = require('fs');
let path = require('path');
//异步删除文件夹 promise
function preWide(dir){
let arr = [dir];
let index = 0;
while(arr[index]){
let current = arr[index++];
let stat = fs.statSync(current);
if(stat.isDirectory()){
let dirs = fs.readdirSync(current);
arr = [...arr,...dirs.map(d => path.join(current,d))]
}
}
for(var i = arr.length-1;i >= 0;i--){
let p = arr[i];
let stat = fs.statSync(p);
if(stat.isDirectory()){
fs.rmdirSync(p)
}else{
fs.unlinkSync(p)
}
}
}
preWide("a");
复制代码
广度异步删除 promise 若是有人有更好的方法的话,麻烦提供代码,谢谢,这里是我屡次修改的所有代码
let fs = require('fs');
let path = require('path');
let arr = [];
let index = 0;
function preWideDir(dir){
if(arr.length === 0){arr[0] = dir}
let current = dir;
return new Promise((resolve,reject) => {
stat = fs.stat(current,(err,stat) =>{ //判断当前目录状态 异步操做
if(stat.isDirectory()){ //若是是文件夹
let dirs = fs.readdir(current,(err,dirs)=>{ //读取文件夹的内容
dirs = dirs.map(d => path.join(current,d));
arr = [...arr,...dirs];
if(index < arr.length-1){
index++;
preWideDir(arr[index])
}else{
for(var i = arr.length-1;i >= 0;i--){
let p = arr[i];
let stat = fs.stat(p,(err,stat) =>{
if(stat.isDirectory()){
fs.rmdir(p,resolve)
}else{
fs.unlink(p, resolve)
}
});
}
}
})
}else{
if(index < arr.length-1){
index++;
preWideDir(arr[index])
}else{
for(var i = arr.length-1;i >= 0;i--){
let p = arr[i];
let stat = fs.stat(p,(err,stat) =>{
if(stat.isDirectory()){
fs.rmdir(p,resolve)
}else{
fs.unlink(p, resolve)
}
});
}
}
}
})
})
}
preWideDir("src/b.8").then(data=>{
console.log("删除成功")
})
复制代码
广度异步删除 promise
let fs = require('fs');
let path = require('path');
let arr = [];
let index = 0;
function preWideDir(dir){
if(arr.length === 0){arr[0] = dir}
let current = dir;
return new Promise((resolve,reject) => {
fs.stat(current,(err,stat) =>{ //判断当前目录状态 异步操做
index++;
if(stat.isDirectory()){ //若是是文件夹
fs.readdir(current,(err,dirs)=>{ //读取文件夹的内容
dirs = dirs.map(d => path.join(current,d));
arr = [...arr,...dirs];
dirs = dirs.map(p => preWideDir(p));
if(index == arr.length-1){
for(let i = arr.length -1;i >= 0;i--){
let p = arr[i];
console.log(arr)
fs.stat(p,(err,stat) =>{
if(stat.isDirectory()){
console.log(p + stat.isDirectory())
fs.rmdir(p,resolve)
}else{
fs.unlink(p,resolve)
}
});
}
}
})
fs.rmdir(dir,resolve)
}
})
})
}
preWideDir("src/a.7").then(data=>{
console.log("删除成功")
})
复制代码
let fs = require('fs');
let path = require('path');
let arr =[];
function preWideDir(dir){
if(!arr[dir]) arr.push(dir);
console.log(dir)
return new Promise((resolve,reject) => {
fs.stat(dir,(err,stat) =>{ //判断当前目录状态 异步操做
if(stat.isDirectory()){ //若是是文件夹
fs.readdir(dir,(err,dirs)=>{ //读取文件夹的内容
dirs = dirs.map(d => path.join(dir,d));
arr = [...arr,...dirs];
dirs = dirs.map(p => preWideDir(p));
Promise.all(dirs).then(()=>{
for(let i = arr.length - 1;i >= 0;i--){
let p = arr[i];
let stat =
fs.stat(p,(err,stat) =>{
if (err) {
console.log("找不到文件"+p);
return;
}
if(stat.isDirectory()){
fs.rmdir(p,resolve)
}else{
fs.unlink(p,resolve)
}
});
}
})
})
}else{
resolve();
}
})
})
}
preWideDir("a.4").then(data=>{
console.log("删除成功")
})
复制代码
广度异步删除
let fs = require('fs');
let path = require('path');
let arr =[];
function prew(dir,callback){
let arr = [dir];
function next(index){
if(arr[index]){
fs.stat(arr[index],(err,stat) =>{
if(err) {
return;
};
if(stat.isDirectory()){
fs.readdir(arr[index],(err,dirs)=>{ //读取文件夹的内容
if(dirs.length === 0){
next(++index);
return;
}
arr = [...arr,...dirs.map(d => path.join(arr[index],d)) ];
console.log(arr.length)
next(++index);
})
}else{
next(++index);
}
})
}else{
for(let i = arr.length - 1;i >= 0;i--){
let p = arr[i];
fs.stat(p,(err,stat) =>{
if (err) {
console.log("找不到文件"+p);
return;
}
if(stat.isDirectory()){
fs.rmdir(p,null)
}else{
fs.unlink(p,null)
}
});
if(i == 0){callback()}
}
}
}
next(0);
}
prew("a.22" ,()=>{
console.log("删除成功")
})
复制代码