C++基礎教程面向對象(學習筆記(107))

隨機文件I / O

文件指針

每個文件流類都包含一個文件指針,用於跟蹤文件中的當前讀/寫位置。當從文件中讀取或寫入某些內容時,讀取/寫入將發生在文件指針的當前位置。默認情況下,打開文件進行讀取或寫入時,文件指針將設置爲文件的開頭。但是,如果在附加模式下打開文件,則文件指針將移動到文件末尾,因此寫入不會覆蓋文件的任何當前內容。

使用seekg()和seekp()進行隨機文件訪問

到目前爲止,我們所做的所有文件訪問都是順序的,也就是說,我們已按順序讀取或寫入文件內容。但是,也可以進行隨機文件訪問,也就是說,跳過文件中的各個點來讀取其內容。當您的文件已滿,並且您希望檢索特定記錄時,這可能很有用。您可以直接跳到要檢索的記錄,而不是在找到所需記錄之前讀取所有記錄。

通過使用seekg()函數(用於輸入)和seekp()函數(用於輸出)操作文件指針來完成隨機文件訪問。如果你想知道,g代表“get”而p代表“put”。對於某些類型的流,seekg()(更改讀取位置)和seekp()(更改寫入位置)獨立運行,但是,對於文件流,讀取和寫入位置始終相同,因此seekg和seekp可以是可互換使用。

seekg()和seekp()函數有兩個參數。第一個參數是一個偏移量,用於確定移動文件指針的字節數。第二個參數是Ios標誌,指定偏移參數應偏移的內容。

Ios seek flag 含義
beg 偏移量相對於文件的開頭(默認)
cur 偏移量相對於文件指針的當前位置
end 偏移量相對於文件末尾

正偏移意味着將文件指針移向文件末尾,而負偏移意味着將文件指針移向文件的開頭。

這裏有些例子:

inf.seekg(14, ios::cur); //向前移動14個字節 
inf.seekg(-18, ios::cur); //向後移動18個字節  
inf.seekg(22, ios::beg); // 在文件中移動到第22個字節
inf.seekg(24); // 在文件中想移動到第24個字節
inf.seekg(-28, ios::end); // 移到文件結尾前的第28個字節

移動到文件的開頭或結尾很容易:

inf.seekg(0, ios::beg); // 移到文件的開頭
inf.seekg(0, ios::end); // 移到文件末尾

讓我們使用seekg()和我們在上一課中創建的輸入文件做一個例子。該輸入文件如下所示:

This is line 1
This is line 2
This is line 3
This is line 4
這是一個例子:

int main()
{
    using namespace std;
 
    ifstream inf("Sample.dat");
 
    //如果我們無法打開輸入文件流進行讀取
    if (!inf)
    {
        // 打印錯誤並退出
        cerr << "Uh oh, Sample.dat could not be opened for reading!" << endl;
        exit(1);
    }
 
    string strData;
 
    inf.seekg(5); // 移動到第5個字符
    // 獲取剩餘的內容並打印出來
    getline(inf, strData);
    cout << strData << endl;
 
    inf.seekg(8, ios::cur); // 將8個字節移動到文件中
    // 獲取剩餘的內容並打印出來
    getline(inf, strData);
    cout << strData << endl;
 
    inf.seekg(-15, ios::end); // 在文件末尾向前移動15個字節
    // 獲取剩餘的內容並打印出來
    getline(inf, strData);
    cout << strData << endl;
    
	return 0;
}

這會產生結果:

is line 1
line 2
his is line 4
注意:一些編譯器在與文本文件結合使用時(由於緩衝)具有seekg()和tellg()的錯誤實現。如果您的編譯器是其中之一(並且您將知道因爲您的輸出將與上面的不同),您可以嘗試以二進制模式打開文件:

ifstream inf("Sample.dat", ifstream::binary);

另外兩個有用的函數是tellg()和tellp(),它們返回文件指針的絕對位置。這可用於確定文件的大小:

ifstream inf("Sample.dat");
inf.seekg(0, ios::end); // 移到文件末尾
cout << inf.tellg();

這打印:

64
這是sample.dat以字節爲單位的長度(假設在最後一行之後返回一個回車符)。

使用fstream同時讀取和寫入文件

fstream類能夠同時讀取和寫入文件!這幾乎是這裏最大的警告,不可能在任意讀寫之間切換。一旦發生讀或寫,在兩者之間切換的唯一方法是執行修改文件位置的操作(例如,搜索)。如果你實際上不想移動文件指針(因爲它已經在你想要的位置),你可以隨時尋找當前位置:

//假設iofile是fstream類型的對象
iofile.seekg(iofile.tellg(), ios::beg); // 尋求當前的文件位置

如果你不這樣做,可能會發生任何奇怪和奇怪的事情。

注意:雖然看起來似乎iofile.seekg(0, ios::cur)也可行,但似乎有些編譯器可能會對此進行優化。

另一個棘手的問題:與ifstream不同,我們可以說while (inf)確定是否有更多要閱讀,這對fstream不起作用。

讓我們使用fstream做一個文件I / O示例。我們將編寫一個程序來打開文件,讀取其內容,並將它找到的任何元音更改爲“#”符號。

int main()
{
    using namespace std;
 
	//注意我們必須同時指定in和out,因爲我們正在使用fstream
    fstream iofile("Sample.dat", ios::in | ios::out);
 
    // 如果我們無法打開iofile,請輸出錯誤
    if (!iofile)
    {
        // 打印錯誤並退出
        cerr << "Uh oh, Sample.dat could not be opened!" << endl;
        exit(1);
    }
 
    char chChar; //我們將按字符分類
 
    // 雖然仍有數據需要處理
    while (iofile.get(chChar))
    {
        switch (chChar)
        {
            //如果我們找到一個元音
            case 'a':
            case 'e':
            case 'i':
            case 'o':
            case 'u':
            case 'A':
            case 'E':
            case 'I':
            case 'O':
            case 'U':
 
                // 備份一個character
                iofile.seekg(-1, ios::cur);
 
                // 因爲我們進行了搜索,所以我們現在可以安全地進行寫操作了
                //讓我們在元音上寫一個#
                iofile << '#';
 
                //現在我們想回到讀取模式,以便下次調用
                // get()將正確執行. 我們將seekg()用於當前位置,
                // 因爲我們不想移動文件指針。
                iofile.seekg(iofile.tellg(), ios::beg);
 
                break;
        }
    }
 
    return 0;
}

其他有用的文件功能

要刪除文件,只需使用remove()函數即可。

此外,如果流當前打開,則is_open()函數將返回true,否則返回false。

關於編寫指向磁盤的指針的警告

雖然將變量傳輸到文件非常簡單,但在處理指針時事情會變得更加複雜。請記住,指針只是保存它指向的變量的地址。雖然可以讀取和寫入磁盤的地址,但這樣做非常危險。這是因爲變量的地址可能因執行而異。因此,雖然當您將該地址寫入磁盤時,變量可能已經存在地址0x0012FF7C,但當您重新讀取該地址時,它可能不再存在於那裏!

例如,假設您有一個名爲nValue的整數,它位於地址0x0012FF7C處。您爲nValue指定了值5.您還聲明瞭一個名爲* pnValue的指針指向nValue。pnValue保存nValue的地址0x0012FF7C。您希望以後保存這些,因此將值5和地址0x0012FF7C寫入磁盤。

幾周後,再次運行程序並從磁盤讀取這些值。您將值5讀入另一個名爲nValue的變量,該變量位於0x0012FF78。您將地址0x0012FF7C讀入名爲* pnValue的新指針。因爲當nValue位於0x0012FF78時pnValue現在指向0x0012FF7C,pnValue不再指向nValue,並且嘗試訪問pnValue會導致您遇到麻煩。

規則:不要將地址寫入文件。當您從磁盤讀回其值時,最初位於這些地址的變量可能位於不同的地址,並且地址將無效。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章