C++入門 -- 文件流操作


參考

計算機科學的角度來看,所有的文件都是由二進制位組成的,都是二進制文件。文本文件和其他二進制文件只是格式不同而已。

C++ 標准庫中還專門提供了 3 個類用於實現文件操作,它們統稱為文件流類,這 3 個類分別為:

  • ifstream:專用於從文件中讀取數據;
  • ofstream:專用於向文件中寫入數據;
  • fstream:既可用於從文件中讀取數據,又可用於向文件中寫入數據。

以上三個類都在<fstream>頭文件中,三個類的繼承關系如下:

 

 

  <iostream> 頭文件中定義有 ostream 和 istream 類的對象 cin 和 cout 不同,<fstream> 頭文件中並沒有定義可直接使用的 fstream、ifstream 和 ofstream 類對象。

  因此,如果我們想使用該類操作文件,需要自己創建相應類的對象。

為什么 C++ 標准庫不提供現成的類似 fin 或者 fout 的對象呢?其實很簡單,文件輸入流和輸出流的輸入輸出設備是硬盤中的文件,硬盤上有很多文件,到底應該使用哪一個呢?所以,C++ 標准庫就把創建文件流對象的任務交給用戶了。

fstream 類擁有 ifstream 和 ofstream 類中所有的成員方法,以下羅列了 fstream 類一些常用的成員方法。

示例:

 1 #include <iostream>
 2 #include <fstream>
 3 
 4 using namespace std;
 5 
 6 int main() {
 7     const char *url = "tianyu";
 8     //創建一個fstream對象-文件流
 9     fstream f1;
10     //將test.txt文件與f1文件流關聯
11     f1.open("test.txt", ios::out);
12     //向test.txt文件中寫入url字符串
13     f1.write(url, 10);
14     f1.close();
15     return 0;
16 }

執行程序,該程序同目錄下會生成一個 test.txt 文件,該文件的內容為:

tianyu

 

open打開文件

void open(const char* szFileName, int mode)
//參數1:指向文件名的指針
//參數2:打開模式標記

文件打開模式標記“:

 示例:流對象打開文件

#include <iostream>
#include <fstream>

using namespace std;

int main() {
    //使用流對象的open成員函數打開文件
    ifstream inFile("inFile.txt", ios::in);
    if(inFile) 
        inFile.close();
    else
        cout << "inFile.txt doesn't exist" << endl;
    //使用流對象的構造函數打開文件
    ofstream oFile("inFile.txt", ios::out);
    if(!oFile)
        cout << "error 1" << endl;
    else
        oFile.close();
    
    fstream oFile2("test1.txt", ios::in|ios::out|ios::trunc);
    oFile2.close();

    return 0;
}

 

close() 關閉文件流

在實際進行文件操作的過程中,對於打開的文件,要及時調用 close() 方法將其關閉,否則很可能會導致讀寫文件失敗。

示例:

 1 #include <iostream>     //std::cout
 2 #include <fstream>      //std::ofstream
 3 using namespace std;
 4 
 5 int main()
 6 {
 7     const char * url = "http://c.biancheng.net/cplus/";
 8     //以文本模式打開out.txt
 9     ofstream destFile("out.txt", ios::out);
10     if (!destFile) {
11         cout << "文件打開失敗" << endl;
12         return 0;
13     }
14     //向out.txt文件中寫入 url 字符串
15     destFile << url;
16     //程序拋出一個異常
17     throw "Exception";
18     //關閉打開的 out.txt 文件
19     destFile.close();
20     return 0;
21 }

17行拋出一個異常,若不處理即崩潰,導致沒有close。

對於 destFile << url ,會先將字符串寫到輸出流緩沖區,待緩沖區滿或者關閉文件時,數據才會寫入到文件。直到崩潰,close都沒執行

導致字符串一直在緩存區沒有寫入到文件

 

flush()刷新緩沖區

 在很多實際場景中,即便已經對文件執行了寫操作,但后續還可能會執行其他的寫操作。對於這種情況,我們可能並不想頻繁地打開/關閉文件,可以使用 flush() 方法及時刷新輸出流緩沖區,也能起到防止寫入文件失敗的作用。

 1 #include <iostream>     //std::cout
 2 #include <fstream>      //std::ofstream
 3 using namespace std;
 4 
 5 int main()
 6 {
 7     const char * url = "http://c.biancheng.net/cplus/";
 8     //以文本模式打開out.txt
 9     ofstream destFile("out.txt", ios::out);
10     if (!destFile) {
11         cout << "文件打開失敗" << endl;
12         return 0;
13     }
14     //向out.txt文件中寫入 url 字符串
15     destFile << url;
16     //刷新輸出流緩沖區
17     destFile.flush();
18     //程序拋出一個異常
19     throw "Exception";
20     //關閉打開的 out.txt 文件
21     destFile.close();
22     return 0;
23 }

在拋出異常崩潰前,刷新流緩沖區,會將字符串寫入到文件。

C++ 中使用 open() 打開的文件,在讀寫操作執行完畢后,應及時調用 close() 方法關閉文件,或者對文件執行寫操作后及時調用 flush() 方法刷新輸出流緩沖區。

 

文件讀寫方法

1、使用 << 與 >> 實現讀寫文件:使用於文本形式讀寫文件;

2、使用read() 和 write() 成員方法讀寫文件:適用於二進制讀寫文件。

 ostream::write()方法寫文件

1 ostream & write(char* buffer, int count);
2 //buffer 用於指定要寫入文件的二進制數據的起始位置;
3 //count 用於指定寫入字節的個數

函數返回的是引用形式的對象,obj.write() 方法的返回值就是對 obj 對象的引用

注意:write方法會從文件寫指針的位置將二進制數據寫入,默然指向開頭,若以 ios::app方式打開,則指向文件末尾

 istream::read()方法讀文件

1 istream & read(char* buffer, int count);、
2 //buffer 用於指定讀取字節的起始位置
3 //count 指定讀取字節的個數

 

移動和獲取文件讀寫指針(seekp、seekg、tellg、tellp)

ostream & seekp (int offset, int mode);
istream & seekg (int offset, int mode);

mode 代表文件讀寫指針的設置模式,有以下三種選項:

  • ios::beg:讓文件讀指針(或寫指針)指向從文件開始向后的 offset 字節處。offset 等於 0 即代表文件開頭。在此情況下,offset 只能是非負數。
  • ios::cur:在此情況下,offset 為負數則表示將讀指針(或寫指針)從當前位置朝文件開頭方向移動 offset 字節,為正數則表示將讀指針(或寫指針)從當前位置朝文件尾部移動 offset字節,為 0 則不移動。
  • ios::end:讓文件讀指針(或寫指針)指向從文件結尾往前的 |offset|(offset 的絕對值)字節處。在此情況下,offset 只能是 0 或者負數
int tellg();
int tellp();

得到當前讀寫指針的具體位置:

  • ifstream 類和 fstream 類還有 tellg 成員函數,能夠返回文件讀指針的位置;
  • ofstream 類和 fstream 類還有 tellp 成員函數,能夠返回文件寫指針的位置。

要獲取文件長度,可以用 seekg 函數將文件讀指針定位到文件尾部,再用 tellg 函數獲取文件讀指針的位置,此位置即為文件長度。

示例:

 1 std::vector<std::vector<char>> Avs::adjustLUT(
 2     const std::string& calFileURL, const std::vector<std::string>& maskFileURLs,
 3     const std::vector<AVS_ADJUST_S>& adjustParams) {
 4     assert(maskFileURLs.size() == adjustParams.size());
 5 
 6     std::ifstream calFile(calFileURL, std::ios::binary);  //binary:以二進制方式打開文件
 7     assert(calFile.is_open());
 8 
 9     std::vector<char> calBuffer(AVS_CALIBRATION_FILE_LENGTH);
10     calFile.read(calBuffer.data(), calBuffer.size());  //從calFile讀取size()大小的數據到calBuffer
11 
12     //將數個maskFile存入到maskBuffer容器中
13     std::vector<std::vector<char>> maskBuffers;
14     for (auto& url : maskFileURLs) {
15         std::ifstream file(url, std::ios::binary);  //將maskFile與file對象綁定
16         assert(file.is_open());
17         file.seekg(0, file.end);  //將文件指針指向文件尾
18 
19         //創建一個大小為文件長度大小的容器buffer
20         std::vector<char> buffer(file.tellg());  //tellg 函數獲取文件讀指針的位置,此位置即為文件長度
21         file.seekg(0, file.beg);  //文件指針指向文件開始處
22         file.read(buffer.data(), buffer.size());  //從file(maskFile)讀取size大小的數據到buffer
23         maskBuffers.push_back(std::move(buffer));  //每循環一次,在maskBuffer容器后加入一個Buffer
24     }
25     
26     ...
27 }

注意

  1. C++ 標准庫使用比如vector::push_back 等這類函數時,會對參數的對象進行復制,連數據也會復制.這就會造成對象內存的額外創建, 本來原意是想把參數push_back進去就行了,通過std::move,可以避免不必要的拷貝操作。
  2. std::move是將對象的狀態或者所有權從一個對象轉移到另一個對象,只是轉移,沒有內存的搬遷或者內存拷貝所以可以提高利用效率,改善性能.。
  3. 對指針類型的標准庫對象並不需要這么做.

原lvalue值被moved from之后值被轉移,所以為空字符串.。一般在代碼段的末尾使用move(value),被轉移所有權的value不可以再次被使用

move用法示例:

//摘自https://zh.cppreference.com/w/cpp/utility/move
#include <iostream>
#include <utility>
#include <vector>
#include <string>
int main()
{
    std::string str = "Hello";
    std::vector<std::string> v;
    //調用常規的拷貝構造函數,新建字符數組,拷貝數據
    v.push_back(str);
    std::cout << "After copy, str is \"" << str << "\"\n";
    //調用移動構造函數,掏空str,掏空后,最好不要使用str
    v.push_back(std::move(str));
    std::cout << "After move, str is \"" << str << "\"\n";
    std::cout << "The contents of the vector are \"" << v[0]
                                         << "\", \"" << v[1] << "\"\n";
}

輸出:

After copy, str is "Hello"
After move, str is ""
The contents of the vector are "Hello", "Hello"

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM