使用std :: filebuf不反映文件大小调整

时间:2015-04-07 07:33:17

标签: c++ file-io fstream

我创建了一个空文件,我正在尝试调整它的大小。除了打开模式,我的调整大小和创建代码非常相似。初始创建按预期工作。调整大小的代码执行正常,但不反映文件大小调整。

这是我的代码:

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上工作

2 个答案:

答案 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);
}