我创建了一个空文件,我正在尝试调整它的大小。除了打开模式,我的调整大小和创建代码非常相似。初始创建按预期工作。调整大小的代码执行正常,但不反映文件大小调整。
这是我的代码:
void resize()
{
std::filebuf fileBuffer;
fileBuffer.open(filePath, ios::out | ios::binary | ios::app);
if (fileBuffer.is_open())
{
fileBuffer.pubseekoff((2 * fileSize) - 1, ios::beg);
auto x = fileBuffer.sputc(0);
fileBuffer.close();
}
}
int main()
{
std::filebuf fileBuffer;
fileBuffer.open(filePath, ios::out | ios::trunc | ios::binary);
if (fileBuffer.is_open())
{
fileBuffer.pubseekoff(fileSize - 1, ios::beg);
fileBuffer.sputc(0);
fileBuffer.close();
}
resize(); // Doesn't work
resizeWithData(); // Works
}
当我尝试通过将实际数据推入其中来调整文件大小时,如下所示,它可以工作:
void resizeWithData()
{
ofstream fstr(filePath, ios::out | ios::binary | ios::app);
if (fstr.is_open())
{
const auto emptySec = make_unique<char[]>(fileSize);
memset(emptySec.get(), 0, fileSize);
fstr.write(emptySec.get(), fileSize);
fstr.close();
}
}
为什么会出现这种差异?我的实际场景涉及创建和调整真正大的文件(1 GB或更多),所以我希望我可以避免空数据的内存分配,如上面的代码所示(因为它真的很慢),如果可能的话
P.S。:我正在使用VS2013更新4在Windows 7上工作
答案 0 :(得分:1)
resize()
失败的原因是您已在app
模式下打开文件。每次写入此流时,实现都会将写入位置重新同步到文件末尾。 pubseekoff
本身的调用,调用seekoff
,以fseek
定义,不会写任何内容,只是重新定位文件位置指示符。
正如您在main
中打开的文件中所示,没有app
标记sputc
会立即写入当前位置,但这可能无法严格保证。
大多数basic_filebuf
操作是根据C文件函数定义的,这似乎没有提到当文件位置指示符超出结束时调用fputc
时会发生什么。文件。然而,POSIX明确表示这会将零写入该位置,您可能会发现大多数实现都支持此定义。
答案 1 :(得分:1)
不同之处在于,在第一个示例中,您截断了文件。如果您在ios::app
中将ios::trunc
更改为resize
,则文件将具有所需的大小(至少在我的系统上)。有趣的是,我无法在标准中找到它应该如何表现的任何语句,只是它在C风格的api中表现为等效调用的一些语句。对于C风格的API,我只在cpp reference上找到了以下注释:
POSIX允许在现有文件末尾之外进行搜索。如果在此搜索之后执行输出,则从间隙读取的任何内容将返回零字节。在文件系统支持的情况下,这将创建一个稀疏文件。
IMO,最后您的fileBuffer.pubseekoff(fileSize - 1, ios::beg);
电话可能也会失败。
但是,分配1GB的零只是为了将它们转储到文件中是一种浪费。我会使用带有重复写调用的缓冲区:
#include <fstream>
#include <iostream>
#include <array>
using std::ios;
std::string filePath("test.file");
size_t fileSize = 1024*1024;
void resize(const std::string& path, std::size_t newSize){
const std::size_t buffSize = 1024*1024;
std::array<char, buffSize> buff{};
std::filebuf fileBuffer;
fileBuffer.open(path, ios::out | ios::binary | ios::app);
auto reminder = newSize - fileBuffer.pubseekoff(0, ios::cur);
while( reminder > buffSize ){
fileBuffer.sputn(buff.data(), buffSize);
reminder -= buffSize;
}
fileBuffer.sputn(buff.data(), reminder);
fileBuffer.close();
}
int main()
{
std::filebuf fileBuffer;
fileBuffer.open(filePath, ios::out | ios::binary | ios::trunc);
if (fileBuffer.is_open())
{
fileBuffer.pubseekoff(fileSize - 1, ios::beg);
fileBuffer.sputc(0);
fileBuffer.close();
}
resize(filePath, 3500000);
}