您可以通过一次移动一个字节来完成此操作,如下所示。但是,如果您允许更大的缓冲区(一次移动 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()。