我知道ANSI C定义了fopen,fwrite,fread,fclose来修改文件的内容。 但是,在截断文件时,我们必须转向操作系统特定的function,例如Linux上的_chsize_s_()
,Windows上的_chsize_s_()
。 但在我们调用那些特定于操作系统的函数之前,我们必须通过调用fileno
来获取FILE指针的文件句柄,也就是非ANSI-C函数。
我的问题是:截断文件后继续使用FILE*
是否可靠? 我的意思是,ANSI C FILE
层有自己的缓冲区,不知道文件是从下面截断的。 如果缓冲的字节超出截断点,那么在执行fclose()
是否会将缓冲的内容刷新到文件中?
如果不能保证,在编写Windows-Linux可移植程序时,使用文件I / O函数和截断操作的最佳做法是什么?
类似的问题:当从fileno
返回的文件句柄查询文件大小时,它是否是我稍后调用fclose()
时的准确大小 – 没有进一步的fwrite()
?
[编辑2012-12-11]
根据约书亚的建议。 我得出结论,当前可能的最佳实践是:通过调用setbuf(stream, NULL);
将流设置为无缓冲模式setbuf(stream, NULL);
,然后truncate()
或_chsize_s()
可以与流和平地工作。
无论如何,没有官方文档似乎明确证实了这种行为,无论是Microsoft CRT还是GNU glibc。
POSIX方式……
ftruncate()
是你正在寻找的,它自2001年以来一直在POSIX基本规范中,所以现在应该在每个现代POSIX兼容系统中。
请注意, ftruncate()
操作POSIX文件描述符(尽管它可能具有误导性的名称),而不是STDIO流FILE
句柄。 另请注意,对STDIO流和对打开流的文件描述符进行操作的底层OS调用的混合操作可能会混淆STDIO库的内部运行时状态。
因此,要在STDIO中安全地使用ftruncate()
,如果您的程序可能已经写入了相关的流,则可能需要首先刷新任何STDIO缓冲区(使用fflush()
)。 这将避免STDIO在截断完成后尝试将其他未写入的缓冲区刷新到文件中。
然后,您可以在STDIO流的FILE
句柄上使用fileno()
来查找打开的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: %sn", argv[0], strerror(errno)); exit(1); } /* fseek() is not necessary here since we truncated at the current offset */
另请注意, ftruncate()
的POSIX定义说“ 不应通过调用ftruncate()修改查找指针的值 ”,这意味着您可能还需要使用fseek()
来设置STDIO层(并且因此间接地将文件描述符)或者文件的新的一端,或者可能返回到文件的开头,或者仍然在文件的边界内的某个地方,根据需要。 (注意,如果使用ftello()
找到截断点,则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
指示器。
其他类型的系统可能具有与POSIX不兼容的不同底层文件I / O接口,同时仍提供兼容的ISO C STDIO库。 从理论上讲,如果这样的系统提供类似于fileno()
和ftrunctate()
那么也可以使用类似的程序,只要我们采取同样的措施避免混淆STDIO库的内部运行时状态。
关于查询文件大小….
您还询问通过查询fileno()返回的文件描述符找到的文件大小是否是成功调用fclose()
后文件大小的准确表示,即使没有进一步调用fwrite()
。
答案是: 不要那样做!
如上所述,如果您不想混淆STDIO库的内部运行时状态,则必须非常小心地使用作为STDIO流打开的文件的POSIX文件描述符。 我们可以在这里补充说,重要的是不要混淆自己。
找到作为STDIO流打开的文件的当前大小的最正确方法是寻找它的末尾,然后通过仅使用STDIO函数询问流指针的位置。
是不是无缓冲的零字节写入应该截断该文件?
有关如何设置unbuffered: ANSI C中的无缓冲I / O,请参阅此问题
需要了解更多c/c++开发分享是否有保证和安全的方法从ANSI C FILE指针截断文件?,也可以关注C/ C++技术分享栏目—计算机技术网(www.ctvol.com)!
以上就是c/c++开发分享是否有保证和安全的方法从ANSI C FILE指针截断文件?相关内容,想了解更多C/C++开发(异常处理)及C/C++游戏开发关注计算机技术网(www.ctvol.com)!)。
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/c-cdevelopment/980338.html