很久之前,我和Swalky在寫HuffmanTree壓縮的時(shí)候,遇到了一個(gè)問(wèn)題:我們想在一個(gè)已經(jīng)寫入了一些內(nèi)容的文件中部(或頭部)寫一些內(nèi)容(用于修改文件的一些meta信息),結(jié)果發(fā)現(xiàn)總是不行。如果用ofstream的默認(rèn)構(gòu)造函數(shù),文件原有內(nèi)容就不會(huì)保留下來(lái),如果用了ios::app,無(wú)論怎么用seekp來(lái)定位,所寫的內(nèi)容都會(huì)跟在文件原有內(nèi)容的最后面。怎么辦呢? 本著RTFM的心態(tài),他去看C++ Primer,我則去看TCPL,以及網(wǎng)上的C++ Reference( http://www./reference/ ): modeFlags describing the requested i/o mode for thefile. This is an object of type ios_base::openmode, which consists on acombination of one or more of the following flags defined as memberconstants: flag valueopening mode|
app | (append) Set the stream's position indicator to the end of the stream before each output operation. | ate | (at end) Set the stream's position indicator to the end of the stream on opening. | binary | (binary) Consider stream as binary rather than text. | in | (input) Allow input operations on the stream. | out | (output) Allow output operations on the stream. | trunc | (truncate) Any current content is discarded, assuming a length of zero on opening. |
我們注意到一個(gè)重要的區(qū)別:app會(huì)在每次寫操作之前都把寫指針置于文件末尾,而ate模式則只在打開(kāi)時(shí)才將寫指針置于文件末尾。于是我們非常興奮地將ofstream置于ios::ate,結(jié)果發(fā)現(xiàn)seekp仍然不能正常工作。 于是我把TCPL的《流》一章反復(fù)讀了幾遍,尤其很認(rèn)真地看了流的緩沖區(qū)streambuf的實(shí)現(xiàn),我突然意識(shí)到,如果不賦予流讀文件的能力,沒(méi)有讀的緩沖區(qū),流就無(wú)法seekp到文件的中部。 我試著改用這段代碼來(lái)構(gòu)造流: - fstream(filename, ios::in|ios::out|ios::ate)
fstream(filename, ios::in|ios::out|ios::ate) 程序的運(yùn)行成功了!我很興奮,因?yàn)楫?dāng)時(shí)是通過(guò)對(duì)流的實(shí)現(xiàn)的分析推斷出這個(gè)結(jié)論的。 后來(lái)有一次有人在群上問(wèn)C中如何這么做,我經(jīng)過(guò)一番實(shí)驗(yàn),發(fā)現(xiàn)只有以r+模式打開(kāi)文件,fseek才起作用。這其實(shí)仍是基于同樣的原理。這里把C的fopen文檔貼出來(lái): modeC 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 withthe same name already exists its content is erased and the file istreated 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 andthe file is treated as a new empty file. | 'a+' | Open a file for reading and appending. All writingoperations are performed at the end of the file, protecting theprevious content to be overwritten. You can reposition (fseek, rewind)the internal pointer to anywhere in the file for reading, but writingoperations will move it back to the end of file. The file is created ifit does not exist |
r+的意思是同時(shí)讀寫,而且該文件必須已經(jīng)存在。用w+是錯(cuò)誤的,因?yàn)樗鼤?huì)把現(xiàn)存文件的所有內(nèi)容清空。 最后附上當(dāng)時(shí)的測(cè)試代碼(用一個(gè)宏開(kāi)關(guān)來(lái)分別測(cè)試C和C++): - #include
- #include
- #include
- #include
-
- 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 <>
-
- fout.close();
-
- #define TESTING_CPP 1
- #if TESTING_CPP
- fout.open(filename, ios::in|ios::out|ios::ate);
-
- fout.seekp(7);
-
- fout <>
- 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_castchar>(fout.get());
- }
-
- return 0;
- }
#include #include #include #include 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=""><>(fout.get()); } return 0;}
|