使用相同的 fstream 读写同一个文件

20

我有一个文件已经包含了一些数据(比如说,8 kB)。我想要从文件的开头读取一些内容,然后从读取结束的位置开始覆盖数据。所以我尝试使用以下代码:

std::fstream stream("filename", std::ios::in | std::ios::out | std::ios::binary);

char byte;
stream.read(&byte, 1);

// stream.seekp(1);

int bytesCount = 4096;

auto bytesVec = std::vector<char>(bytesCount, 'c');
char* bytes = bytesVec.data();

std::cout << stream.bad() << std::endl;

stream.write(bytes, bytesCount);

std::cout << stream.bad() << std::endl;

如果我运行这段代码,第一个 bad() 返回 false,但第二个返回 true,实际上什么也没有被写入。

如果我将 bytesCount 减少到小于 4096(大概是某个内部缓冲区的大小),第二个 bad() 返回 false,但仍然没有任何东西被写入。

如果我取消注释 seekp() 行,写入操作开始工作: bad() 返回 false 并且字节确实被写入。

为什么此处需要 seekp()?为什么不使用它就无法工作?使用 seekp() 是正确的方法吗?

我正在使用 Windows 7 上的 Visual Studio 2012。

2个回答

31
您正在违反MS的fstream库从其C <stdio.h>实现继承的限制,该限制涉及以更新模式打开的文件中读取和写入操作的混合。
C标准(我引用了C99,但在此点上与C89没有区别)在7.19.5.3/6处规定:
当使用更新模式(上述模式参数值列表中的第二或第三个字符为'+')打开文件时,可以在关联流上执行输入和输出。但是,在没有调用fflush函数或文件定位函数(fseek、fsetpos或rewind)的情况下,不得直接跟随输出进行输入,且除非输入操作遇到文件结束,否则不得直接跟随输出进行输入。 (我强调)。
因此,您的stream.seekp(1)解决方案,归结为C的fseek,是正确的。
GNU C库没有这个标准限制,因此当使用GCC构建时,您发布的代码将按预期工作。
MS的<fstream>库在遵循C ++标准时继承了此限制。 fstream是使用basic_filebuf<charT,traits>实现的。在(C ++11)标准的帐户中,该模板在§ 27.9.1.1/2处简单地声明:
使用类basic_filebuf对象控制的序列进行读取和写入的限制与使用标准C库文件进行读取和写入相同。

1
这个问题对于C++流也有文档吗?或者微软的实现在这方面是否遵循标准? - svick

3

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接