c++读取utf8文件_Node.js 进阶之 fs 文件模块学习
前言
文件操作是開(kāi)發(fā)過(guò)程中并不可少的一部分。Node.js 中的 fs 模塊是文件操作的封裝,它提供了文件讀取、寫(xiě)入、更名、刪除、遍歷目錄、鏈接等 POSIX 文件系統(tǒng)操作。與其它模塊不同的是,fs 模塊中所有的操作都提供了異步和同步的兩個(gè)版本,具有 sync 后綴的方法為同步方法,不具有 sync 后綴的方法為異步方法。
文章概覽(本文能學(xué)到)
- 計(jì)算機(jī)中關(guān)于系統(tǒng)和文件的一些常識(shí)-- 權(quán)限位 mode-- 標(biāo)識(shí)位 flag-- 文件描述符 fs
- Node.js 中 fs 模塊的 api 詳細(xì)講解與對(duì)應(yīng) Demo-- 常規(guī)文件操作-- 高級(jí)文件操作-- 文件目錄操縱
- Node.js 中 fs 模塊的 api 對(duì)應(yīng) demo
- fs 模塊的應(yīng)用場(chǎng)景及實(shí)戰(zhàn)訓(xùn)練(大小文件實(shí)現(xiàn)拷貝)
面試會(huì)問(wèn)
說(shuō)幾個(gè)fs模塊的常用函數(shù)?什么情況下使用fs.open的方式讀取文件?用fs模塊寫(xiě)一個(gè)大文件拷貝的例子(注意大文件)?
文件常識(shí)
計(jì)算機(jī)中的一些文件知識(shí),文件的權(quán)限位 mode、標(biāo)識(shí)位 flag、文件描述符 fd 等你有必要了解下。這些內(nèi)容對(duì)于你接下來(lái)學(xué)習(xí) fs 的 api ,記憶和使用都會(huì)有很多幫助。
權(quán)限位 mode
因?yàn)?fs 模塊需要對(duì)文件進(jìn)行操作,會(huì)涉及到操作權(quán)限的問(wèn)題,所以需要先清楚文件權(quán)限是什么,都有哪些權(quán)限。
文件權(quán)限表:
在上面表格中,我們可以看出系統(tǒng)中針對(duì)三種類型進(jìn)行權(quán)限分配,即文件所有者(自己)、文件所屬組(家人)和其他用戶(陌生人),文件操作權(quán)限又分為三種,讀、寫(xiě)和執(zhí)行,數(shù)字表示為八進(jìn)制數(shù),具備權(quán)限的八進(jìn)制數(shù)分別為 4、2、1,不具備權(quán)限為 0。
為了更容易理解,我們可以隨便在一個(gè)目錄中打開(kāi) Git,使用 Linux 命令 ls -al 來(lái)查目錄中文件和文件夾的權(quán)限位
drwxr-xr-x 1 koala 197121 0 Jun 28 14:41 core-rw-r--r-- 1 koala 197121 293 Jun 23 17:44 index.md在上面的目錄信息當(dāng)中,很容易看出用戶名、創(chuàng)建時(shí)間和文件名等信息,但最重要的是開(kāi)頭第一項(xiàng)(十位的字符)。
第一位代表是文件還是文件夾,d 開(kāi)頭代表文件夾,- 開(kāi)頭的代表文件,而后面九位就代表當(dāng)前用戶、用戶所屬組和其他用戶的權(quán)限位,按每三位劃分,分別代表讀(r)、寫(xiě)(w)和執(zhí)行(x),- 代表沒(méi)有當(dāng)前位對(duì)應(yīng)的權(quán)限。
權(quán)限參數(shù) mode 主要針對(duì) Linux 和 Unix 操作系統(tǒng),Window 的權(quán)限默認(rèn)是可讀、可寫(xiě)、不可執(zhí)行,所以權(quán)限位數(shù)字表示為 0o666,轉(zhuǎn)換十進(jìn)制表示為 438。
標(biāo)識(shí)位 flag
Node.js 中,標(biāo)識(shí)位代表著對(duì)文件的操作方式,如可讀、可寫(xiě)、即可讀又可寫(xiě)等等,在下面用一張表來(lái)表示文件操作的標(biāo)識(shí)位和其對(duì)應(yīng)的含義。
符號(hào)含義r讀取文件,如果文件不存在則拋出異常。r+讀取并寫(xiě)入文件,如果文件不存在則拋出異常。rs讀取并寫(xiě)入文件,指示操作系統(tǒng)繞開(kāi)本地文件系統(tǒng)緩存。w寫(xiě)入文件,文件不存在會(huì)被創(chuàng)建,存在則清空后寫(xiě)入。wx寫(xiě)入文件,排它方式打開(kāi)。w+讀取并寫(xiě)入文件,文件不存在則創(chuàng)建文件,存在則清空后寫(xiě)入。wx+和 w+ 類似,排他方式打開(kāi)。a追加寫(xiě)入,文件不存在則創(chuàng)建文件。ax與 a 類似,排他方式打開(kāi)。a+讀取并追加寫(xiě)入,不存在則創(chuàng)建。ax+與 a+ 類似,排他方式打開(kāi)。
上面表格就是這些標(biāo)識(shí)位的具體字符和含義,但是 flag 是不經(jīng)常使用的,不容易被記住,所以在下面總結(jié)了一個(gè)加速記憶的方法。
- r:讀取
- w:寫(xiě)入
- s:同步
- +:增加相反操作
- x:排他方式
r+ 和 w+ 的區(qū)別,當(dāng)文件不存在時(shí),r+ 不會(huì)創(chuàng)建文件,而會(huì)拋出異常,但 w+`` 會(huì)創(chuàng)建文件;如果文件存在,r+不會(huì)自動(dòng)清空文件,但w+` 會(huì)自動(dòng)把已有文件的內(nèi)容清空。
文件描述符 fs
操作系統(tǒng)會(huì)為每個(gè)打開(kāi)的文件分配一個(gè)名為文件描述符的數(shù)值標(biāo)識(shí),文件操作使用這些文件描述符來(lái)識(shí)別與追蹤每個(gè)特定的文件,Window 系統(tǒng)使用了一個(gè)不同但概念類似的機(jī)制來(lái)追蹤資源,為方便用戶,Node.js 抽象了不同操作系統(tǒng)間的差異,為所有打開(kāi)的文件分配了數(shù)值的文件描述符。
在 Node.js 中,每操作一個(gè)文件,文件描述符是遞增的,文件描述符一般從 3 開(kāi)始,因?yàn)榍懊嬗?0、1、2 三個(gè)比較特殊的描述符,分別代表 process.stdin(標(biāo)準(zhǔn)輸入)、process.stdout(標(biāo)準(zhǔn)輸出)和 process.stderr(錯(cuò)誤輸出)。
文件操作
完整性讀寫(xiě)文件操作
文件讀取-fs.readFile
fs.readFile(filename,[encoding],[callback(error,data)]
文件讀取函數(shù)
demo:
const?fs?=?require('fs');const?path?=?require('path');const?filePath?=?path.join(__dirname,'koalaFile.txt')const?filePath1?=?path.join(__dirname,'koalaFile1.txt')//?--?異步讀取文件fs.readFile(filePath,'utf8',function(err,data){????console.log(data);//?程序員成長(zhǎng)指北});//?--?同步讀取文件const?fileResult=fs.readFileSync(filePath,'utf8');console.log(fileResult);//?程序員成長(zhǎng)指北文件寫(xiě)入fs.writeFile
fs.writeFile(filename,data,[options],callback)文件寫(xiě)入操作
這個(gè)時(shí)候第一章節(jié)講的計(jì)算機(jī)知識(shí)就用到了,flag 值,默認(rèn)為 w,會(huì)清空文件,然后再寫(xiě)。flag 值,r代表讀取文件,w 代表寫(xiě)文件,a 代表追加。
demo:
//?寫(xiě)入文件內(nèi)容(如果文件不存在會(huì)創(chuàng)建一個(gè)文件)//?寫(xiě)入時(shí)會(huì)先清空文件fs.writeFile(filePath,?'寫(xiě)入成功:程序員成長(zhǎng)指北',?function(err)?{????if?(err)?{????????throw?err;????}????//?寫(xiě)入成功后讀取測(cè)試????var?data=fs.readFileSync(filePath,?'utf-8');????console.log('new?data?-->'+data);});//?通過(guò)文件寫(xiě)入并且利用flag也可以實(shí)現(xiàn)文件追加fs.writeFile(filePath,?'程序員成長(zhǎng)指北追加的數(shù)據(jù)',?{'flag':'a'},function(err)?{?????if?(err)?{?????????throw?err;?????}?????console.log('success');?????var?data=fs.readFileSync(filePath,?'utf-8')?????//?寫(xiě)入成功后讀取測(cè)試?????console.log('追加后的數(shù)據(jù)?-->'+data);?});文件追加-appendFile
fs.appendFile(filename,?data,?[options],?callback)說(shuō)明:該方法以異步的方式將 data 插入到文件里,如果文件不存在會(huì)自動(dòng)創(chuàng)建
demo:
//?--?異步另一種文件追加操作(非覆蓋方式)//?寫(xiě)入文件內(nèi)容(如果文件不存在會(huì)創(chuàng)建一個(gè)文件)fs.appendFile(filePath,?'新數(shù)據(jù)程序員成長(zhǎng)指北456',?function(err)?{????if?(err)?{????????throw?err;????}????//?寫(xiě)入成功后讀取測(cè)試????var?data=fs.readFileSync(filePath,?'utf-8');????console.log(data);});//?--?同步另一種文件追加操作(非覆蓋方式)fs.appendFileSync(filePath,?'同步追加一條新數(shù)據(jù)程序員成長(zhǎng)指北789');拷貝文件-copyFile
fs.copyFile(filenameA,?filenameB,callback)刪除文件-unlink
fs.unlink(filename,?callback)demo:
//?--?異步文件刪除fs.unlink(filePath,function(err){?if(err)?return;});//?--?同步刪除文件fs.unlinkSync(filePath,function(err){????if(err)?return;});指定位置讀寫(xiě)文件操作(高級(jí)文件操作)
接下來(lái)的高級(jí)文件操作會(huì)與上面有些不同,流程稍微復(fù)雜一些,要先用fs.open來(lái)打開(kāi)文件,然后才可以用fs.read去讀,或者用fs.write去寫(xiě)文件,最后,你需要用fs.close去關(guān)掉文件。
特殊說(shuō)明:read 方法與 readFile 不同,一般針對(duì)于文件太大,無(wú)法一次性讀取全部?jī)?nèi)容到緩存中或文件大小未知的情況,都是多次讀取到 Buffer 中。想了解 Buffer 可以看Node進(jìn)階-探究不在V8堆內(nèi)存中存儲(chǔ)的Buffer對(duì)象
文件打開(kāi)-fs.open
fs.open(path,flags,[mode],callback)第一個(gè)參數(shù):文件路徑 第二個(gè)參數(shù):與開(kāi)篇說(shuō)的標(biāo)識(shí)符 flag 相同 第三個(gè)參數(shù):[mode] 是文件的權(quán)限(可選參數(shù),默認(rèn)值是0666) 第四個(gè)參數(shù):callback 回調(diào)函數(shù)
demo:
fs.open(filePath,'r','0666',function(err,fd){???console.log('哈哈哈',fd);?//返回的第二個(gè)參數(shù)為一個(gè)整數(shù),表示打開(kāi)文件返回的文件描述符,window中又稱文件句柄})demo 說(shuō)明:返回的第二個(gè)參數(shù)為一個(gè)整數(shù),表示打開(kāi)文件返回的文件描述符,window中又稱文件句柄,在開(kāi)篇也有對(duì)文件描述符說(shuō)明。
文件讀取-fs.read
fs.read(fd,?buffer,?offset,?length,?position,?callback);六個(gè)參數(shù)
demo:
const?fs?=?require('fs');let?buf?=?Buffer.alloc(6);//?創(chuàng)建6字節(jié)長(zhǎng)度的buf緩存對(duì)象//?打開(kāi)文件fs.open('6.txt',?'r',?(err,?fd)?=>?{??//?讀取文件??fs.read(fd,?buf,?0,?3,?0,?(err,?bytesRead,?buffer)?=>?{????console.log(bytesRead);????console.log(buffer);????//?繼續(xù)讀取????fs.read(fd,?buf,?3,?3,?3,?(err,?bytesRead,?buffer)?=>?{??????console.log(bytesRead);??????console.log(buffer);??????console.log(buffer.toString());????});??});});//?3//?//?3//?//?你好文件寫(xiě)入-fs.write
fs.write(fd,?buffer,?offset,?length,?position,?callback);六個(gè)參數(shù)
文件關(guān)閉-fs.close
fs.close(fd,callback)demo:
//?注意文件描述符fdfs.open(filePath,?'r',?(err,?fd)?=>?{??fs.close(fd,?err?=>?{????console.log('關(guān)閉成功');//?關(guān)閉成功??});});目錄(文件夾)操作
1、fs.mkdir 創(chuàng)建目錄
fs.mkdir(path,?[options],?callback)demo:
fs.mkdir('./mkdir',function(err){??if(err)?return;??console.log('創(chuàng)建目錄成功');})注意:在 Windows 上,在根目錄上使用 fs.mkdir() (即使使用遞歸參數(shù))也會(huì)導(dǎo)致錯(cuò)誤:
fs.mkdir('/',?{?recursive:?true?},?(err)?=>?{??//?=>?[Error:?EPERM:?operation?not?permitted,?mkdir?'C:']});2、fs.rmdir刪除目錄
fs.rmdir(path,callback)注意:在文件(而不是目錄)上使用 fs.rmdir() 會(huì)導(dǎo)致在 Windows 上出現(xiàn) ENOENT 錯(cuò)誤、在 POSIX 上出現(xiàn) ENOTDIR 錯(cuò)誤。
3、fs.readdir讀取目錄
fs.readdir(path,?[options],?callback)demo:
const?fs?=?require('fs');fs.readdir('./file',function(err,data){??if(err)?return;??//data為一個(gè)數(shù)組??console.log('讀取的數(shù)據(jù)為:'+data[0]);});實(shí)戰(zhàn)訓(xùn)練:
只講文件相關(guān) Api 顯得很枯燥,下面說(shuō)一些 fs 在 Node.js 中的具體應(yīng)用
「示例:fs 模塊如何實(shí)現(xiàn)文件拷貝」
文件拷貝例子包括小文件拷貝和大文件拷貝(之前講的 fs 模塊也可以實(shí)現(xiàn)文件拷貝)
小文件拷貝
小文件拷貝除了上面 fs 自己提供的 api 我們自己也可以通過(guò)讀寫(xiě)完成一個(gè)拷貝例子,如下:
//?文件拷貝?將?`data.txt`?文件中的內(nèi)容拷貝到?`copyData.txt`//?讀取文件const?fileName1?=?path.resolve(__dirname,?'data.txt')fs.readFile(fileName1,?function?(err,?data)?{????if?(err)?{????????//?出錯(cuò)????????console.log(err.message)????????return????}????//?得到文件內(nèi)容????var?dataStr?=?data.toString()????//?寫(xiě)入文件????const?fileName2?=?path.resolve(__dirname,?'copyData.txt')????fs.writeFile(fileName2,?dataStr,?function?(err)?{????????if?(err)?{????????????//?出錯(cuò)????????????console.log(err.message)????????????return????????}????????console.log('拷貝成功')????})})我們使用 readFile 和 writeFile 實(shí)現(xiàn)了一個(gè) copy 函數(shù),那個(gè) copy 函數(shù)是將被拷貝文件的數(shù)據(jù)一次性讀取到內(nèi)存,一次性寫(xiě)入到目標(biāo)文件中,這種針對(duì)小文件還好。
大文件拷貝
如果是一個(gè)大文件幾百M(fèi)一次性讀取寫(xiě)入不現(xiàn)實(shí),所以需要多次讀取多次寫(xiě)入,接下來(lái)使用文件操作的高級(jí)方法對(duì)大文件和文件大小未知的情況實(shí)現(xiàn)一個(gè) copy 函數(shù)。當(dāng)然除了這種方式還有我在之前的文章講過(guò)的 stream 模塊也可以實(shí)現(xiàn),而且性能更好,但是這里就不再重復(fù)說(shuō)明,本篇主要講 fs 模塊。
demo:
//?copy?方法function?copy(src,?dest,?size?=?16?*?1024,?callback)?{??//?打開(kāi)源文件??fs.open(src,?'r',?(err,?readFd)?=>?{????//?打開(kāi)目標(biāo)文件????fs.open(dest,?'w',?(err,?writeFd)?=>?{??????let?buf?=?Buffer.alloc(size);??????let?readed?=?0;?//?下次讀取文件的位置??????let?writed?=?0;?//?下次寫(xiě)入文件的位置??????(function?next()?{????????//?讀取????????fs.read(readFd,?buf,?0,?size,?readed,?(err,?bytesRead)?=>?{??????????readed?+=?bytesRead;??????????//?如果都不到內(nèi)容關(guān)閉文件??????????if?(!bytesRead)?fs.close(readFd,?err?=>?console.log('關(guān)閉源文件'));??????????//?寫(xiě)入??????????fs.write(writeFd,?buf,?0,?bytesRead,?writed,?(err,?bytesWritten)?=>?{????????????//?如果沒(méi)有內(nèi)容了同步緩存,并關(guān)閉文件后執(zhí)行回調(diào)????????????if?(!bytesWritten)?{??????????????fs.fsync(writeFd,?err?=>?{????????????????fs.close(writeFd,?err?=>?return?!err?&&?callback());??????????????});????????????}????????????writed?+=?bytesWritten;????????????//?繼續(xù)讀取、寫(xiě)入????????????next();??????????});????????});??????})();????});??});}在上面的 copy 方法中,我們手動(dòng)維護(hù)的下次讀取位置和下次寫(xiě)入位置,如果參數(shù) readed 和 writed 的位置傳入 null,NodeJS 會(huì)自動(dòng)幫我們維護(hù)這兩個(gè)值。
現(xiàn)在有一個(gè)文件 6.txt 內(nèi)容為 “你好”,一個(gè)空文件 7.txt,我們將 6.txt 的內(nèi)容寫(xiě)入 7.txt 中。
const?fs?=?require('fs');//?buffer?的長(zhǎng)度const?BUFFER_SIZE?=?3;//?拷貝文件內(nèi)容并寫(xiě)入copy('6.txt',?'7.txt',?BUFFER_SIZE,?()?=>?{??fs.readFile('7.txt',?'utf8',?(err,?data)?=>?{????//?拷貝完讀取?7.txt?的內(nèi)容????console.log(data);?//?你好??});});在 Node.js 中進(jìn)行文件操作,多次讀取和寫(xiě)入時(shí),一般一次讀取數(shù)據(jù)大小為 64k,寫(xiě)入數(shù)據(jù)大小為 16k。
END!
總結(jié)
以上是生活随笔為你收集整理的c++读取utf8文件_Node.js 进阶之 fs 文件模块学习的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: python html转换为普通文本_将
- 下一篇: C++ STL 容器的一些总结 ---