如何使用c++中file stream


??在c++中引入了stream堤瘤,一開始接觸這個的時候感覺無法正確的理解stream的用法,在寫項目的時候要用到讀寫文件,慢慢理解了stream的一些基本的用法。

  • overview

首先我們來看一下fstream的基本信息:


??可以看到fstream是從iostream中繼承來的霎箍,那么iostream的方法fstream闯冷,我們再來看看c++中提供的I/O類:


圖中表明了各個類的關系榨咐,我們主要來看看fstream中的內(nèi)容璧亚,fstream有4個類:

  • ifstream
  • fstream
  • ofstream
  • filebuf

其中前三個類中ifstreamofstream的意思從字面上很好理解:

  • ifstream將文件作為輸入的源
  • ofstream將文件作為輸出destination
  • fstream在定義文件流對象是可以自己設置為in或者out

定義了文件流對象后,每個文件流對象維護自己的filebuf,filebuf是與文件關聯(lián)的緩沖區(qū)柱搜,filebuf對象在其內(nèi)部是通過操作一個中間的輸入/輸出緩沖區(qū)迟郎,該文件內(nèi)容的改變會與其同步(在對文件操作完后顯示的調(diào)用sync或者文件關閉之后)

  • 不同I/O類型的關系

  • 從概念上來說,不同的設備和不同的char(char聪蘸、wchar)的size對I/O操作沒有影響宪肖。例如:

我們可以用cin從控制臺、文件健爬、string中獲取輸入控乾;
????同理也可以用cout向不同控制臺、文件娜遵、string中輸出

c++抹平了這些是通過繼承實現(xiàn)的蜕衡,ifstreamistringstream是從istream中繼承來的,那么繼承保證我們在使用ifstreamistringstream時好像在使用istream(cin)一樣魔熏。

  • I/O類不允許拷貝和賦值(將拷貝構造和賦值構造聲明為私有方法并不予以實現(xiàn))

  • fstream

我們來看一個簡單的示例程序:

    #include <iostream>
    #include <fstream>
    int main(int argc, char **argv)
    {
        std::ofstream my_file;
        my_file.open("example.txt");
        my_file << "Contents from ofstream.\n"
        my_file.close()
        return 0;
    } 

以上的代碼會往example.txt文件(若當前路徑下存在則直接寫入,不存在則會創(chuàng)建對應的文件)中寫入我們要寫的內(nèi)容鸽扁,和cout向屏幕輸出一樣蒜绽,只是這里屏幕變成了文件而已。
從以上的代碼入手桶现,我們來一步一步看看文件流的用法躲雅。

  • 打開文件

開文件就是我們需要將文件與文件對象關聯(lián),一個打開的文件在程序中的呈現(xiàn)方式是以文件流對象存在的骡和,對流對象的任何操作都會應用到物理文件上相赁,打開一個文件的動作是:
open(filename, mode)

其中filename是文件名,類型是字符串慰于,mode是一個可選的參數(shù)钮科,可以從以下組合中選擇:

fstream mode

以上的每個可選參數(shù)可以通過位操作的|來組合,例如我們想將文件以二進制的形式打開婆赠,并向其尾巴部添加內(nèi)容:
????std::ofstream my_file("example.txt", ios::out | ios::app | ios::binary);

每一個fstream都有自己的默認模式:

default mode

通過以上的表格绵脯,ifstreamofstream即使在定義的時候,其mode參數(shù)默認為in或者out,對于fstream蛆挫,默認的mode被應用只在fstreammode參數(shù)沒有傳入任何值的時候才會組合ios::inios::out,但是只要mode中有一個參數(shù)被選擇赃承,那么默認的參數(shù)就會被重寫,不考慮先前的組合的mode

'fstream'分別定義了三個構造函數(shù)會自動的調(diào)用open()函數(shù)悴侵,我們可以這樣來定義一個fstream對象:

    std::fstream my_file("example.txt", ios::out | ios::app | ios::binary);

但是打開文件可能會出現(xiàn)打不開的異常情況瞧剖,標準庫提供了一個函數(shù)is_open()去檢測文件是否打開成功:

    if(my_file.is_open()){
        /*open is successful, proceed with output*/
    }
  • 關閉文件

就像我們開水龍頭必然要關水龍頭一樣,我們打開文件必須要關閉文件可免,文件一直打開會消耗系統(tǒng)資源抓于。在我們完成了對我文件的輸入或輸出操作后關閉文件后,對應的文件資源變得可用。
??my_file.close();
以上的close()調(diào)用將會刷新與之相關的緩沖區(qū)并將文件關閉巴元。在關閉之后其原先的文件流對象可以綁定其他文件了毡咏,并且該文件可以被其他進程打開。值得注意的是逮刨,在實際的編寫代碼的過程中我們經(jīng)常會忘記調(diào)用close()呕缭,但是在文件流對象被析構的時候與之關聯(lián)的文件也會被關閉,但是我認為自己主動去關閉文件是個好習慣修己,最好不要把這個任務交給編譯器恢总。

  • 文本文件

在打開文件的的模式中不加入ios::binary的打開方式是以文本的方式去打開文件,在這里存在一個問題是睬愤,對于格式化輸出(\n片仿、\t等轉(zhuǎn)移字符)的文本,會有一個轉(zhuǎn)換的過程尤辱,這里的行為就像cout向屏幕輸出一樣砂豌。

    #include <iostream>
    #include <fstream>
    int main(int argc, char **argv)
    {
        std::ofstream my_file("example.txt");
        if(my_file.is_open()){
            my_file << "This is a line.\n";
            my_file << "This is another line.\n";
        }else{
            std::cout <<"Unable to open file";
        }
        return 0;
    }

文件的內(nèi)容如下:
example.txt:

This is a line.
This is another line.

從文件中輸入的行為類似與我們從cin中獲取輸入的行為類似,這里不舉例光督。

  • 檢測打開文件的文件流對象的狀態(tài)

標準庫提供了一系列成員函數(shù)去檢測文件流對象的狀態(tài)阳距。

bad()

對文件的讀寫操作失敗時返回true,例如,當我們試圖去向一個未打開的文件執(zhí)行寫入操作或者向文件寫入時文件空間不足的時候會返回true

fail()

行為和bad()類似结借,但是在在讀寫文件內(nèi)容筐摘,內(nèi)容格式的錯誤的時候也會返回true,例如我們?nèi)プx一個整數(shù)的時候船老,但是一個字母被讀入的時候咖熟,這時候'fail()'會返回true

eof()

讀文件到文件尾的時候返回true

good()

在以上介紹的所有狀態(tài)中只要有一個返回truegood()就返回false,注意good()bad()不是相反的操作,相對于bad(),good()檢測的狀態(tài)更多柳畔。
成員函數(shù)clear()用于重置以上的狀態(tài)馍管。

  • 獲取和設置文件流對象的位置

所有的流對象都會在其內(nèi)部至少維護一個指示讀寫位置的動作。
ifstreamistream一樣維護一個內(nèi)部的get薪韩,來獲取當前文件的讀入的位置咽斧,ofstream反之亦然堪置,而fstream維護了一個getput的動作。
對于當前文件流讀寫的位置可以通過以下的成員函數(shù)獲取或者修改张惹。

tellg() && tellp()

這兩個函數(shù)返回一個類型為streampos的值舀锨,這個類型的值表示當前的位置,其中get position(tellg()返回)或表示put position(tellp()返回)

seekg() && seekp()

以上函數(shù)允許改變當前的getput position宛逗,其函數(shù)調(diào)用被重載:
seekg(position)
seekp(position)
以上兩個函數(shù)改變了stream pointerposition的絕對位置(從文件的開始位置算起)坎匿,其參數(shù)的類型是streampos

另外兩個重載函數(shù)的原型為;
seekg(offset, direction)
seekp(offset, direction)
以上是改變stream position的相對位置雷激,以direction為參考對象作偏移替蔬,其中direction的類型是seekdir,是一個枚舉類型屎暇,其值有以下的值可選:


| ios::beg | offset counted from the beginning of the stream|
| ios::cur | offset counted from the curent position???????|
| ios::end | offset counted from the end of the stream?????|

    /**********************************
    * @Brief: Obtaining the file size
    * @CreatedTime:29/5/16
    **********************************/
    #include <iostream>
    #include <fstream>
    int main(int argc, char **argv)
    {
        std::streampos begin, end;
        std::ifstream my_file("example.bin", ios::binary);
        begin = my_file.tellg()  // get the beginning position
        my_file.seekg(0, ios::end);  // set the stream pointer to end position
        end = my_file.tellg();  // get the end position
        my_file.close();  // close the opening file
        std::cout << "Size is: " << (end - begin) << "bytes.\n";  // calculate the file size
        return 0;
    }

在以上代碼中我們注意到有streampos類型的值承桥,streampos類型是專門為緩沖區(qū)和stream pointerposition使用的,具有相同類型的值可以做減法根悼,也可以轉(zhuǎn)換為整型凶异。

  • 二進制文件

二進制的文件不同于文本文件,文本文件讀寫其內(nèi)容時有其格式挤巡,例如我們可以利用getline獲取每一行的內(nèi)容剩彬,但是首先對于二進制文件來說getline()來讀取二進制文件效率不高,還有就是二進制的數(shù)據(jù)可能不是按照行的格式存儲矿卑。
??file stream為二進制的數(shù)據(jù)設計了兩個成員函數(shù)讀寫二進制文件喉恋,read()write()是讀寫二進制文件的操作,其中write()ostream的成員函數(shù)母廷,fstreamostream中繼承得到這個函數(shù);read()函數(shù)是istream的成員函數(shù)轻黑,ifstream從其繼承這個函數(shù); fstream的對象同時擁有這兩個函數(shù),其中readwrite函數(shù)的原型如下:
write(memory_block, size)
read(memory_block, size)
read()write()中的memory_block是一個字節(jié)數(shù)組(char*)琴昆,表示要讀入或者寫入的字節(jié)塊氓鄙,其中size是一個整型表示讀取或者要寫入的字節(jié)數(shù)。

    #include <iostream>
    #include <fstream>
    int main(int argc, char **argv)
    {
        std::streampos size;
        char *memory_block;
        std::ifstream file("example.txt", ios::in | ios::bnary | ios::ate);
        if(file.is_open()){
            size = file.tellg();
            memory_block = new char[size];
            file.seekg(0, ios::beg);
            file.read(memory_block, size);
            file.close();
            std::cout << "the entire file is in memory";
            delete[] memory_block;
        }else{
            std::cout << "unable to open the file";
       }
        return 0;
    }

通過以上的程序我們將文件的內(nèi)容放入內(nèi)存中(char數(shù)組)椎咧,由于無法事先確定文件的大小玖详,所以我們需要動態(tài)分配這個數(shù)組的大小把介,一開始我們打開文件的時候我們將position指針放在了文件內(nèi)容的末尾勤讽,當調(diào)用tellg的時候我們可以可以得到文件內(nèi)容的大小。在分配了整個數(shù)組后拗踢,我們將positon指針放回到文件開始脚牍,將文件讀入。

  • 緩沖和同步

當我們使用流對象時巢墅,在file stream和物理文件之間存在一個類型為streambuf的緩沖對象诸狭,例如券膀,我們有一個ofstream的對象,我們調(diào)用put的時候每次會向這個中間的buffer插入一個字節(jié)驯遇,而不是直接插入到與之關聯(lián)的文件中芹彬。

當緩沖區(qū)刷新的時候,緩沖區(qū)中的內(nèi)容會寫入到與之關聯(lián)的文件中叉庐,當以下事件發(fā)生時舒帮,緩沖區(qū)會刷新:
**文件關閉: **在文件關閉之前,緩沖區(qū)中還沒有刷新的數(shù)據(jù)會同步陡叠,待處理的數(shù)據(jù)寫入文件或先從文件中讀取玩郊。
**緩沖區(qū)溢出: **當緩沖區(qū)溢出的時候會自動同步。
**手動同步: **顯式的執(zhí)行flush或者endl會刷新緩沖區(qū)枉阵。
**調(diào)用成員函數(shù)同步: ** 對應的文件流對象調(diào)用sync()的方法就可以同步緩存區(qū)译红,當同步失敗的時候返回-1,同步成功返回0.


相關資料:
Input/output with files
C++ Primer 5th


Keep focus and have fun

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市兴溜,隨后出現(xiàn)的幾起案子侦厚,更是在濱河造成了極大的恐慌,老刑警劉巖昵慌,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件假夺,死亡現(xiàn)場離奇詭異,居然都是意外死亡斋攀,警方通過查閱死者的電腦和手機已卷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來淳蔼,“玉大人侧蘸,你說我怎么就攤上這事○睦妫” “怎么了讳癌?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長存皂。 經(jīng)常有香客問我晌坤,道長,這世上最難降的妖魔是什么旦袋? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任骤菠,我火速辦了婚禮,結果婚禮上疤孕,老公的妹妹穿的比我還像新娘商乎。我一直安慰自己,他們只是感情好祭阀,可當我...
    茶點故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布鹉戚。 她就那樣靜靜地躺著鲜戒,像睡著了一般。 火紅的嫁衣襯著肌膚如雪抹凳。 梳的紋絲不亂的頭發(fā)上遏餐,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天,我揣著相機與錄音赢底,去河邊找鬼境输。 笑死,一個胖子當著我的面吹牛颖系,可吹牛的內(nèi)容都是我干的嗅剖。 我是一名探鬼主播,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼嘁扼,長吁一口氣:“原來是場噩夢啊……” “哼信粮!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起趁啸,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤强缘,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后不傅,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體旅掂,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年访娶,在試婚紗的時候發(fā)現(xiàn)自己被綠了商虐。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡崖疤,死狀恐怖秘车,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情劫哼,我是刑警寧澤叮趴,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站权烧,受9級特大地震影響眯亦,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜般码,卻給世界環(huán)境...
    茶點故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一妻率、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧侈询,春花似錦舌涨、人聲如沸糯耍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至革为,卻和暖如春扭粱,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背震檩。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工琢蛤, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人抛虏。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓博其,卻偏偏與公主長得像,于是被迫代替她去往敵國和親迂猴。 傳聞我的和親對象是個殘疾皇子慕淡,可洞房花燭夜當晚...
    茶點故事閱讀 44,611評論 2 353

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn)沸毁,斷路器峰髓,智...
    卡卡羅2017閱讀 134,651評論 18 139
  • https://nodejs.org/api/documentation.html 工具模塊 Assert 測試 ...
    KeKeMars閱讀 6,327評論 0 6
  • 轉(zhuǎn)自 http://www.ibm.com/developerworks/cn/education/java/j-...
    抓兔子的貓閱讀 2,304評論 0 22
  • C/C++輸入輸出流總結 前兩天寫C++實習作業(yè),突然發(fā)現(xiàn)I/O是那么的陌生息尺,打了好長時間的文件都沒有打開携兵,今天終...
    LuckTime閱讀 1,729評論 0 6
  • c++文件操作詳解 C++ 通過以下幾個類支持文件的輸入輸出: ofstream: 寫操作(輸出)的文件類 (由o...
    鮑陳飛閱讀 1,774評論 0 2