ofstream 与 ate 的故事

发布于 2022-12-23 19:00:41 字数 3753 浏览 107 评论 0

很久之前,我和 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:

  • 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;  
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

文章
评论
28 人气
更多

推荐作者

櫻之舞

文章 0 评论 0

弥枳

文章 0 评论 0

m2429

文章 0 评论 0

野却迷人

文章 0 评论 0

我怀念的。

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文