ofstream與ate的故事

很久之前,我和Swalky在寫Huffman Tree壓縮的時候,遇到了一個問題:我們想在一個已經寫入了一些內容的文件中部(或頭部)寫一些內容(用於修改文件的一些meta信息),結果發現總是 不行。如果用ofstream的默認構造函數,文件原有內容就不會保留下來,如果用了ios::app,無論怎麼用seekp來定位,所寫的內容都會跟在 文件原有內容的最後面。怎麼辦呢?

本着RTFM的心態,他去看C++ Primer,我則去看TCPL,以及網上的C++ Reference( http://www.cplusplus.com/reference/ ):

mode
Flags describing the requested i/o mode for the file. This is an object of type ios_base::openmode, which consists on a combination of one or more of the following flags defined as member constants:
flag value opening mode
app (app end) Set the stream's position indicator to the end of the stream before each output operation.
ate (at e nd) Set the stream's position indicator to the end of the stream on opening.
binary (binary ) Consider stream as binary rather than text.
in (in put) Allow input operations on the stream.
out (out put) Allow output operations on the stream.
trunc (trunc ate) Any current content is discarded, assuming a length of zero on opening.

我們注意到一個重要的區別:app會在每次寫操作之前都把寫指針置於文件末尾,而ate模式則只在打開時纔將寫指針置於文件末尾。於是我們非常興奮地將ofstream置於ios::ate,結果發現seekp仍然不能正常工作。

於是我把TCPL的《流》一章反覆讀了幾遍,尤其很認真地看了流的緩衝區streambuf的實現,我突然意識到,如果不賦予流讀文件的能力,沒有讀的緩衝區,流就無法seekp到文件的中部。

我試着改用這段代碼來構造流:

 

fstream(filename, ios::in|ios::out|ios::ate)
 

 

程序的運行成功了!我很興奮,因爲當時是通過對流的實現的分析推斷出這個結論的。

後來有一次有人在羣上問C中如何這麼做,我經過一番實驗,發現只有以r+模式打開文件,fseek才起作用。這其實仍是基於同樣的原理。這裏把C的fopen文檔貼出來:

mode
C string containing a file access modes. It can be:
"r" Open a file for reading. The file must exist.
"w" Create an empty file for writing. If a file with the same name already exists its content is erased and the file is treated as a new empty file.
"a" Append to a file. Writing operations append data at the end of the file. The file is created if it does not exist.
"r+" Open a file for update both reading and writing. The file must exist.
"w+" Create an empty file for both reading and writing. If a file with the same name already exists its content is erased and the file is treated as a new empty file.
"a+" Open a file for reading and appending. All writing operations are performed at the end of the file, protecting the previous content to be overwritten. You can reposition (fseek , rewind ) the internal pointer to anywhere in the file for reading, but writing operations will move it back to the end of file. The file is created if it does not exist

r+的意思是同時讀寫,而且該文件必須已經存在。用w+是錯誤的,因爲它會把現存文件的所有內容清空。

最後附上當時的測試代碼(用一個宏開關來分別測試C和C++):

 

#include <iostream>
#include <fstream>
#include <string>
#include <cstdio>

using namespace std;

int main()
{
    const char * original = "012345678901234567890123456789"; //30 chars
    const char * overwrite = "abcdeabcde";
    const char * filename = "test.txt";

    fstream fout;

    fout.open(filename, ios::out|ios::trunc); //destroy any current content

    fout << original;

    fout.close();

#define TESTING_CPP 1
#if TESTING_CPP
    fout.open(filename, ios::in|ios::out|ios::ate);

    fout.seekp(7);

    fout << overwrite;
    fout.close();
#else
    FILE * fout_c;

    if(fout_c = fopen(filename, "r+"))
    {
        fseek(fout_c, 7, SEEK_SET);
        fprintf(fout_c, overwrite);
        fclose(fout_c);
    }
#endif //TESTING_CPP

    fout.open(filename, ios::in);

    while(!fout.eof())
    {
        cout << static_cast<char>(fout.get());
    }

    return 0;
}
 

 

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