【问题标题】:C - Reading then writing to a file without using memory (in place)?C - 在不使用内存(就地)的情况下读取然后写入文件?
【发布时间】:2017-09-19 02:34:21
【问题描述】:

我需要向左“移动”一个大字符串 X 空格。它太大了,无法放入内存,所以我需要在原地完成它。 我需要使用最少的系统调用来做到这一点。

我知道我可以使用缓冲区和重用内存来最大程度地减少内存消耗,然后使用fseek -> 读取 -> 写入直到我完成,但我很想看看是否可以这样做是可能的。

【问题讨论】:

  • 你考虑过mmap吗?
  • 这可能取决于他所考虑的“记忆”。 mmap 仅限于 size_t 字节,除非他指的是堆栈,否则肯定会适合内存。

标签: c string file system-calls


【解决方案1】:

您可以通过一次移动一个字节来完成此操作,如下所示。但是,如果您允许更大的缓冲区(一次移动 4096 字节),您将获得更好的性能。我们显然使用了一些堆栈内存,但它不会根据您的前缀大小或文件大小进行分配,因此我们可以称之为“就地”。

void inPlaceTruncate(
    char const * const filename,
    int shift)
{
  FILE * f;
  if ((f = fopen(filename, "r+")) == NULL) {
    // handle error
  }

  // go to the end
  if (fseek(f, 0, SEEK_END) != 0) {
    // handle error
  }

  // get current file size
  long int const oldFileLen = ftell(f);
  if (oldFileLen < 0) {
    // handle error
  } else if (oldFileLen < shift) {
    // make the file empty
    shift = oldFileLen;
  }

  // go back to the beginning
  if (fseek(f, 0, SEEK_SET) != 0) {
    // handle error
  }

  // move file
  char buffer;
  for (long int pos = 0; pos < oldFileLen-shift; ++pos) {
    // slow to be repeatedly fseeking...
    if (fseek(f, pos+shift, SEEK_SET) != 0) {
      // handle error
    }

    if (fread(&buffer, sizeof(buffer), 1, f) != 1) {
      // handle error
    }

    if (fseek(f, pos, SEEK_SET) != 0) {
      // handle error
    }

    if (fwrite(&buffer, sizeof(buffer), 1, f) != 1) {
      // handle error
    }
  }

  // shrink file -- in a rather unpleasent way
  #ifdef WIN32
  if (_chsize(fileno(f), oldFileLen-shift) != 0) {
    // handle error
  }
  #else
  // we're assuming if its not windows, it's at least posix compliant.
  if (ftruncate(fileno(f), oldFileLen-shift) != 0) {
    // handle error
  }
  #endif

  fclose(f);
}

一个相关的post 用于文件收缩。

编辑以实际回答 OP 的问题。

已编辑以注释错误处理的位置。

此外,正如 cmets 中所指出的,这只会处理小于 2GB 的文件和班次。为了处理更大的文件并解决fseek()/ftell()/ftruncate() 32 位限制(在 Windows 上可以使用_chsize_s),您需要使用相对偏移量在循环中确定文件大小,并多次致电ftruncate()

【讨论】:

  • 嗯。此代码将所有内容向右移动。 OP似乎希望将文件的内容向左移动,使文件更小。这是一个不同的、稍微棘手的问题。
  • ftell() returns long,不是int
  • 更好。 int 可能无法保存来自ftell() 的返回值;如果文件足够大,这很重要。因此,oldFileLenshiftpos 都应该是 long。此外,应检查fopen() 的返回值以确定文件是否成功打开;应检查所有对fseek()fread()fwrite() 的调用是否有错误。
  • 一般来说,文件大小限制可以通过使用lseek64()而不是lseek()来绕过
猜你喜欢
  • 1970-01-01
  • 2015-04-25
  • 2018-01-06
  • 2014-02-02
  • 2019-11-29
  • 2016-04-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多