Cpp淺析系列-STL之fstream

前言

本地每一次測(cè)試都要從控制臺(tái)輸入測(cè)試數(shù)據(jù),久而久之,博主就很煩了。

所以就想著要了解文件的輸入輸出流,把測(cè)試數(shù)據(jù)放在txt文檔里,直接從文檔里讀取。

Iostream

從控制臺(tái)獲取信息并輸出到控制臺(tái)。不是本文的重點(diǎn),附上例子帶過就好。

提一句,一定要寫上命名空間,否則不能直接用cincout!

#include?<iostream>
using?namespace?std;
void?cinAndCout(){
????cout<<"請(qǐng)輸入信息:"<<endl;
????string?a;
????cin>>a;
????cout<<"你輸出的信息為:?"<<a;
}
int?main()?{
????cinAndCout();
?return?0;
}
image-20220828220137609

Fstream

iostream是可以從鍵盤獲取輸入信息;而fstream則是可以從TXT等文件中獲得信息的。必須要包含下面的兩個(gè)頭文件才行。

#include?<iostream>
#include?<fstream>

建立連接

使用open方法建立鏈接,文件路徑有兩個(gè)辦法。

  • 單個(gè)項(xiàng)目?jī)?nèi)使用相對(duì)路徑,注意起點(diǎn)是可執(zhí)行文件所在的位置。后來難免會(huì)切換運(yùn)行環(huán)境,甚至是運(yùn)行機(jī)器,所以也會(huì)用絕對(duì)路徑。

  • 絕對(duì)路徑來指明文件,注意路徑中最后不要有空格或中文字符。與宏定義結(jié)合起來更為方便。

也定義下列常量,來標(biāo)明建立的鏈接是哪個(gè)模式的。

常量 解釋
app 每次寫入前尋位到流結(jié)尾
binary 以二進(jìn)制模式打開
in 為讀打開
out 為寫打開
trunc 在打開時(shí)舍棄流的內(nèi)容
ate 打開后立即尋位到流結(jié)尾

后來,為了方便,也可以直接在生成類對(duì)象的時(shí)候直接傳入文件名稱和模式。為了方便理解,就直接附在后文的例子中了。

關(guān)閉鏈接

直接在對(duì)象上調(diào)用close方法就可以了。

常規(guī)操作

讀取文件

ifstream?fin("input.txt");
//fstream?fin("input.txt",?ios::in);//等價(jià)于上一行
????if?(!fin)?{
????????cout?<<?"打開文件出錯(cuò)"?<<?endl;
????????return?;
????}

逐行讀入

逐行讀入,是需要引入string.h頭文件的。而且逐行讀取的字符串,是沒有換行符的!

string?s;
while(getline(fin,s))
{
????cout<<s<<endl;
}

逐個(gè)字符讀入

忽略空格與回車。

char?c;
while?(!fin.eof())
{
????fin?>>?c;
????cout<<c<<endl;
}

逐個(gè)字符讀入

包括空格與回車。

char?c;
fin?>>?noskipws;
while?(!fin.eof())
{
????fin>>c;
????cout<<c<<endl;
}

寫入文件

先打開文件流,往流里面插入數(shù)據(jù)信息,然后關(guān)閉文件流。

#include?<fstream>
#include<iostream>
#define?filepath?"/Volumes/KeenMacPlus/Projects/C++項(xiàng)目/KeenCPPTest-all/STL/fstream/txt/"
using?namespace?std;

/*
?*?向文件中寫入字符串,注意模式的不同?*?*/

void?write(){
????//?ofstream?fout(filepath"input.txt");寫入流,覆蓋模式
????fstream?fin;
????//?fin.open(filepath"input.txt");?//?覆蓋模式
????fin.open(filepath"input.txt",?ios::app);?//?末尾追加模式
????if?(!fin)?{
????????cout?<<?"打開文件出錯(cuò)"?<<?endl;
????}
????fin?<<?"這是新加的一行"?<<?endl;
????fin.close();
}
image-20220828191911146
image-20220828191919756

應(yīng)用

讀取文件行數(shù)

#include<iostream>??
#include<fstream>??

using?namespace?std;

void?read()?{
????//?ifstream?fin("input.txt");//等價(jià)于下兩行
????fstream?fin;
????fin.open(filepath"input.txt",?ios::in);
????if?(!fin)?{
????????cout?<<?"打開文件出錯(cuò)"?<<?endl;
????}

????char?c;
????int?lineNum?=?0;//?統(tǒng)計(jì)行數(shù)
????while?(fin.get(c))//?逐字符讀入
????{
????????if?(c?==?'\n')
????????????lineNum++;
????}
????cout?<<?lineNum?+?1?<<?endl;
????fin.close();
}

重定向

之前是用cincout從控制臺(tái)獲得信息,重定向后可以用這兩個(gè)對(duì)象操作文件寫入寫出!

#include<fstream>
#include?<ostream>
#include?<iostream>
#include<string>

#define?filepath?"/Volumes/KeenMacPlus/Projects/C++項(xiàng)目/KeenCPPTest-all/STL/fstream/txt/"
using?namespace?std;

void?cinAndCout()?{
????cout?<<?"請(qǐng)輸入信息:"?<<?endl;
????string?a;
????cin?>>?a;
????cout?<<?"你輸出的信息為:?"?<<?a;
}

int?main()?{
????//?cinAndCout();

????ifstream?fin(filepath"rdbuf.txt");
????ofstream?fout(filepath"out.txt");
????if?(!fin?||?!fout)?{
????????cout?<<?"打開文件出錯(cuò)"?<<?endl;
????????return?0;
????}

????//?下面四行是用?rdbuf()??重新定向
????streambuf?*cinbackup?=?NULL;
????streambuf?*coutbackup?=?NULL;
????coutbackup?=?cout.rdbuf(fout.rdbuf());
????cinbackup?=?cin.rdbuf(fin.rdbuf());?//?用?rdbuf()??重新定向

????//?原先這個(gè)對(duì)象是輸出到控制臺(tái),限制是輸出到文件out.txt
????cout?<<?"Hello?world"?<<?endl;

????string?line;
????while?(cin)?{
????????cin?>>?line;//?從?rdbuf.txt?文件讀入
????????if?(cin)
????????????cout?<<?line?<<?endl;//?寫入?out
????}
????fout.close();
????fin.close();


????//恢復(fù)標(biāo)準(zhǔn)輸入輸出
????//?如果不恢復(fù),即使關(guān)閉了文件流,仍然無法輸出到控制臺(tái)!
????cin.rdbuf(cinbackup);?//?恢復(fù)鍵盤輸入
????cout.rdbuf(coutbackup);?//?恢復(fù)控制臺(tái)輸出

????cout?<<?"完成操作!";


????return?0;
}

按行號(hào)修改數(shù)據(jù)

一共有兩種實(shí)現(xiàn)邏輯,分別是針對(duì)大文件和小文件的。

不論是修改還是刪除,其實(shí)底層邏輯其實(shí)是一樣的。

第一步是讀取原來的文件,一般是for循環(huán)逐行讀取。

第二步是在循環(huán)的過程中匹配行號(hào)或者關(guān)鍵字,當(dāng)匹配成功就對(duì)內(nèi)容進(jìn)行修改或者舍棄

第三步,則是將讀到內(nèi)存的信息流寫入文件,目標(biāo)可以是新的文件,也可以是原來的文件。

按照緩存數(shù)據(jù)的方式可以分為兩種,一種是針對(duì)大文件,短時(shí)間內(nèi)無法完成全部操作,這時(shí)候我建議使用中間文件做緩存,即使出現(xiàn)意外也有之前留下的內(nèi)容;而對(duì)于小文件,則是可以選擇將文件內(nèi)容全部存到內(nèi)存里面來,然后再一次性覆蓋回原來的文件。

其實(shí)刪除某一行內(nèi)容也是屬于這種操作,整體文件流區(qū)別變化是不大的。

中間文件緩存

/*
?*?修改指定行號(hào)的數(shù)據(jù)為新的內(nèi)容
?*?三個(gè)參數(shù)分別是文件名,行號(hào),要修改的內(nèi)容
?*?底層邏輯是把文件信息全存儲(chǔ)到中間文件里,然后再把中間文件的內(nèi)容覆蓋源文件的內(nèi)容
?*
?*?好處是即使出錯(cuò)了或者斷電了,那么之前的數(shù)據(jù)仍然是保留在中間文件中的
?*?壞處是要讀寫量增大了,很耗費(fèi)時(shí)間,適合大文件?*?*/

void?modiFile(string?filename,?int?lineNum,?string?str)?{
????ifstream?file(filename);
????string?line;?//?臨時(shí)變量,讀取文件的一行內(nèi)容
????int?num?=?0;//?統(tǒng)計(jì)行號(hào)
????ofstream?outfile("test.txt",?ios::out?|?ios::trunc);//用中間文件,記錄整個(gè)文件的信息

????//將信息從源文件寫到中間文件
????while?(!file.eof())?{
????????getline(file,?line);
????????if?(num?!=?lineNum?-?1)
????????????outfile?<<?line?<<?endl;
????????else
????????????outfile?<<?str?<<?endl;
????????num++;
????}
????outfile.close();
????file.close();

????//?將信息從中間文件寫入到源文件
????ofstream?outfile1(filename,?ios::out?|?ios::trunc);
????fstream?file1("test.txt");
????while?(!file1.eof())?{
????????getline(file1,?line);
????????if?(line?!=?"")?{
????????????outfile1?<<?line?<<?endl;
????????}
????}
????outfile1.close();
????file1.close();

????//最后刪除中間文件
????//?system("del?test.txt");//Windows
????system("rm?-f??test.txt");//?mac和linux
}

用變量緩存

/*
?*?修改指定行號(hào)的數(shù)據(jù)為新的內(nèi)容
?*?三個(gè)參數(shù)分別是文件名,行號(hào),要修改的內(nèi)容
?*?底層邏輯是把文件信息全存儲(chǔ)到字符串變量outStr里,然后再覆蓋原先的內(nèi)容
?*
?*?好處是不需要生成另一個(gè)臨時(shí)文件來存儲(chǔ)內(nèi)容,適合小文件。
?*?缺點(diǎn)就是一旦在過程中出錯(cuò)了或者斷電了,那么之前的數(shù)據(jù)就全沒有了!?*?*/

void?modiFile2(string?filename,?int?lineNum,?char?*content)?{
????ifstream?in(filename);
????string?line;?//?臨時(shí)變量,讀取文件的一行內(nèi)容
????int?num?=?0;//?統(tǒng)計(jì)行號(hào)
????string?outStr;//?記錄整個(gè)文件的數(shù)據(jù)流

????while?(getline(in,?line))?{//?逐行讀取,是沒有換行符的!
????????num++;
????????if?(lineNum?!=?num)?{
????????????outStr?+=?line;
????????}?else?{
????????????outStr?+=?content;
????????}
????????outStr?+=?'\n';
????}
????in.close();

????//新建這個(gè)文件的寫入流對(duì)象,調(diào)用flush函數(shù)來清空文件流內(nèi)的內(nèi)容。
????ofstream?out;
????out.open(filename);
????out.flush();
????out?<<?outStr;
????out.close();
}

文件轉(zhuǎn)碼

從GBK轉(zhuǎn)為UTF8

因?yàn)橐郧暗?cpp文件很多都是GBK編碼的,現(xiàn)在為了統(tǒng)一稱為UTF8編碼的。在Linux系統(tǒng)上面有一個(gè)iconv命令。

:?切換到指定路徑
cd?/Volumes/KeenMacPlus/Projects/C++項(xiàng)目/KeenCPPTest-all

:?find命令,在Convert_UTF8的文件夾中生成相同名稱的路徑樹
find?./KeenCPPTest-all?-type?d?-exec?mkdir?-p?Convert_UTF8/{}?\;

:?find命令?-exec?是找到文件后運(yùn)行腳本命令
:?雙引號(hào)中的腳本內(nèi)容,是將文件轉(zhuǎn)碼后放到指定的文件夾下
:?最后?\;?是find?-exec的終止標(biāo)志

find?./KeenCPPTest-all?-exec?sh?-c?"iconv?-f?GBK?-t?utf-8?{}?>?Convert_UTF8/{}"?\;

感謝

源碼文件:

gitee:https://gitee.com/JunKuangKuang/KeenCPPTest-all/tree/main/STL/fstream

github.com:https://github.com/JunKuangKuang/KeenCPPTest-all/blob/main/STL/fstream

參考我以前存留的cpp文件,感謝以前努力的自己。

參考張成的博客

使用iconv批量轉(zhuǎn)碼

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • [轉(zhuǎn)]C/C++ 文件讀寫操作總結(jié) 在編程的過程中,文件的操作是一個(gè)經(jīng)常用到的問題,在C++Builder中,可以...
    天之道天知道閱讀 5,292評(píng)論 0 7
  • 轉(zhuǎn)載(https://www.cnblogs.com/MrYuan/p/5383408.html) 基于C的文件操...
    送分童子笑嘻嘻閱讀 501評(píng)論 0 0
  • ??在c++中引入了stream,一開始接觸這個(gè)的時(shí)候感覺無法正確的理解stream的用法,在寫項(xiàng)目的時(shí)候要用到讀...
    UnderwoodY閱讀 12,464評(píng)論 0 5
  • C++ 文件讀寫 作者:AceTan,轉(zhuǎn)載請(qǐng)標(biāo)明出處! 很多時(shí)候,我們需要數(shù)據(jù)的永久化存儲(chǔ),而不是把數(shù)據(jù)放在內(nèi)存中...
    AceTan閱讀 4,766評(píng)論 5 10
  • 1.流的概念和流類庫(kù)的結(jié)構(gòu) 程序的輸入指的是從輸入文件將數(shù)據(jù)傳送給程序,程序的輸出指的是從程序?qū)?shù)據(jù)傳送給輸出文件...
    無涯之涯閱讀 251評(píng)論 0 0

友情鏈接更多精彩內(nèi)容