在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("刪除成功")
})
複製代碼