在node我們如果操作檔案,就需要讀取buffer.通過fs讀取
快取 Buffer 你知道多少
檔案操作,流的操作都要用到buffer,
一個位元組最大多少? 假設8個1用10進製表示 265,他們分別有一下幾個特點:
2進位制 | 8進位制 | 10進位制 | 16進位制 |
---|---|---|---|
0b開頭 | 0o開頭 | 278 | 0x開頭 |
- 關於進位制的轉換 16 進位制 轉換為 10 進位制
- 關於10進位制 小於127的我們認為是單位元組,大於127的我們認為是漢字和雙位元組
- 將10進位制轉換為任意進位制
(278).tostring(16)
=> 116
比如 unicode編碼都對應著一個utf8
規則
十六進位制 | 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是相同的,我們區分他們用的就是編碼前面的標識。
比如我們要轉換一個 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
;
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方法有哪些
- readFile/ writeFile /copyFile
- read/write/open/sync/colse
- fs.mkdir /rmdir/rname/readfir
- ...
fs模組, 在nodejs中,使用fs模組來實現所有有關檔案及目錄建立,寫入刪除操作,所有方法氛圍同步非同步兩種實現,帶sync為同步,反之非同步,在此之前我們先知道許可權的問題
linux許可權
檔案型別與許可權 | 連結佔用的節點 | 檔案所有者 | 檔案所有者的使用者組 | 檔案大小 | 檔案建立的事件 | 最近修改時間 | 檔名稱 |
---|---|---|---|---|---|---|---|
-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
- w+ 讀取並寫入,如果存在則清空檔案內容,不存在則建立內容
- r+ 讀取並寫入,檔案不存在則報錯
- r 檔案不存在則報錯
- w 檔案不存在則建立,存在則清空
fs檔案操作
fs檔案操作 - read
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();
})
複製程式碼
fs檔案操作 - copy
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.mkdir /rmdir/rname/readfir
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("刪除成功")
})
複製程式碼