POSIX 方式....
ftruncate()是您正在寻找的东西,它自 2001 年以来一直在 POSIX 基本规范中,所以它现在应该在每个现代 POSIX 兼容系统中。
请注意,ftruncate()它对 POSIX 文件描述符进行操作(尽管其名称可能具有误导性),而不是 STDIO 流FILE句柄。另请注意,对 STDIO 流和对打开流的文件描述符进行操作的底层操作系统调用的混合操作可能会混淆 STDIO 库的内部运行时状态。
因此,为了ftruncate()安全地使用 STDIO,如果您的程序可能已经写入有问题的流,则可能需要首先刷新任何 STDIO 缓冲区(使用)。fflush()这将避免 STDIO 在截断完成后尝试将其他未写入的缓冲区刷新到文件中。
然后,您可以使用fileno()STDIO 流的FILE句柄来查找打开的 STDIO 流的底层文件描述符,然后将该文件描述符与ftruncate(). 您可能会考虑将调用放在调用fileno()的参数列表中,ftruncate()这样您就不会保留文件描述符并意外使用它,而其他方式可能会进一步混淆 STDIO 的内部状态。也许像这样(比如将文件截断到当前的 STDIO 流偏移量):
/*
* NOTE: fflush() is not needed here if there have been no calls to fseek() since
* the last fwrite(), assuming it extended the length of the stream --
* ftello() will account for any unwritten buffers
*/
if (ftruncate(fileno(stdout), ftello(stdout)) == -1) {
fprintf(stderr, "%s: ftruncate(stdout) failed: %s\n", argv[0], strerror(errno));
exit(1);
}
/* fseek() is not necessary here since we truncated at the current offset */
另请注意,POSIX 定义ftruncate()说“不应通过调用 ftruncate() 来修改查找指针的值”,因此这意味着您可能还需要使用 usefseek()来设置 STDIO 层(因此间接设置文件描述符) 到文件的新结尾,或者回到文件的开头,或者仍然在文件边界内的某个地方,根据需要。(请注意,fseek()如果使用 找到截断点,则不需要ftello()。)
如果您遵循上述过程,则不必使 STDIO 流无缓冲,尽管这样做当然可以替代使用fflush()(但不是fseek())。
没有 POSIX....
如果您需要遵守严格的 ISO 标准 C,例如 C99,那么您没有可移植的方法将文件截断为除零 (0) 长度之外的给定长度。我在第 7.21.3 节(第 2 段)中提到的 C11 的最新草案:
二进制文件不会被截断,除非在 7.21.5.3 中定义。对文本流的写入是否会导致相关文件在该点之后被截断是实现定义的。
(并且 7.21.5.3 描述了fopen()允许文件被截断为零长度的标志)
关于文本文件的警告就在那里,因为在同时具有文本和二进制文件的愚蠢系统上(而不仅仅是普通的 POSIX 样式的内容不可知文件),那么通常可以将一个值写入将存储在文件中的文件在写入的位置,EOF下次读取文件时将被视为指示符。
其他类型的系统可能具有不同的底层文件 I/O 接口,这些接口与 POSIX 不兼容,但仍提供兼容的 ISO C STDIO 库。理论上,如果这样的系统提供了类似的东西fileno(),ftrunctate()那么类似的过程也可以与它们一起使用,前提是要注意避免混淆 STDIO 库的内部运行时状态。
关于查询文件大小......
您还询问了通过查询 fileno() 返回的文件描述符找到的文件大小是否会在成功调用 后准确表示文件大小fclose(),即使没有进一步调用fwrite().
答案是:不要那样做!
如上所述,如果您不想混淆 STDIO 库的内部运行时状态,则必须非常小心地使用作为 STDIO 流打开的文件的 POSIX 文件描述符。我们可以在这里补充一点,重要的是不要将自己与它混淆。
查找作为 STDIO 流打开的文件的当前大小的最正确方法是查找文件的末尾,然后仅使用 STDIO 函数询问流指针在哪里。