有一个保证和安全的方法来截断从ANSI C FILE指针的文件? [英] Is there a guaranteed and safe way to truncate a file from ANSI C FILE pointer?

查看:139
本文介绍了有一个保证和安全的方法来截断从ANSI C FILE指针的文件?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我知道ANSI C定义了fopen,fwrite,fread,fclose来修改文件的内容。但是,在截断文件时,我们必须转向Linux上特定于操作系统的函数,例如 truncate() _chsize_s _() / code>在Windows上。但是在我们可以调用这些操作系统特定的函数之前,我们必须通过调用 fileno 来获得FILE指针的文件句柄,这也是一个非ANSI-C的文件句柄。 p>

我的问题是:在截断文件后继续使用 FILE * 是否可靠?我的意思是,ANSI C FILE 图层有自己的缓冲区,不知道文件是从下面截断的。如果缓冲的字节超出了截断点,那么在执行 fclose()
$ b $时,缓冲的内容是否会被刷新到文件如果不能保证,在编写Windows-Linux可移植程序时,使用文件I / O函数和截断操作的最佳实践是什么?



类似的问题:当从 fileno 返回的文件句柄中查询文件大小时,以后调用 fclose() - 没有进一步 fwrite()





根据约书亚的建议。我得出结论,目前可能的最佳做法是:通过调用 setbuf(stream,NULL); ,然后 truncate()将流设置为无缓冲模式。 code>或 _chsize_s()可以安全地使用流。

明确地确认这个行为,无论是Microsoft CRT还是GNU glibc。

解决方案

POSIX的方法....



ftruncate()是你正在寻找的东西,自2001年以来它一直在POSIX基础规范中,所以它应该在每一个现在的POSIX兼容系统。

请注意, ftruncate()在POSIX文件描述符上运行可能具有误导性的名称),而不是STDIO流 FILE 句柄。还要注意,对STDIO流和底层操作系统调用的操作在打开流的文件描述符上可能会混淆STDIO库的内部运行时状态。



因此,要使用STDIO安全地使用 ftruncate(),可能需要首先刷新任何STDIO缓冲区(使用 fflush ()),如果你的程序可能已经写入有问题的流。这样可以避免STDIO试图在截断完成之后将其他未写入的缓冲区刷新到文件。



然后,您可以使用 fileno / code>在STDIO流的 FILE 句柄上查找打开的STDIO流的底层文件描述符,然后使用该文件描述符和 ftruncate()。你可能会考虑把 fileno()的调用放在 ftruncate()调用的参数列表中,不要保留文件描述符,并且不小心使用它,否则可能会进一步混淆STDIO的内部状态。也许就像这样(比如截断一个文件到当前的STDIO流偏移量):

pre
注意: fflush()在这里不需要,如果没有调用fseek(),因为
*最后一个fwrite(),假设它延长了流的长度 -
* ftello任何未写入的缓冲区
* /
if(ftruncate(fileno(stdout),ftello(stdout))== -1){
fprintf(stderr,%s:ftruncate(stdout) :%s\\\
,argv [0],strerror(errno));
exit(1);

/ * fseek()在这里是不必要的,因为我们在当前偏移处截断* /

还要注意, ftruncate()的POSIX定义表示查找指针的值不能通过调用ftruncate ),所以这意味着你可能还需要使用use fseek()来设置STDIO层(因此间接地文件描述符)到文件的新结尾,或者可能返回到文件的开头,或者在文件边界内的某个地方。 (请注意,如果使用 ftello()找到截断点,则 fseek() / p>

如果您按照上面的步骤操作,则不需要使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流的文件的POSIX文件描述符如果您不想混淆STDIO库的内部运行时状态,则必须非常小心地使用它。我们可以在这里补充一点,不要把自己弄糊涂了。



查找作为STDIO流打开的文件当前大小的最正确方法是寻找到它的结尾,然后问只用STDIO函数的流指针。

I know ANSI C defines fopen, fwrite, fread, fclose to modify a file's content. However, when it comes to truncating a file, we have to turn to OS specific function, e.g, truncate() on Linux, _chsize_s_() on Windows. But before we can call those OS specific functions, we have to obtain the file-handle from FILE pointer, by calling fileno, also an non-ANSI-C one.

My question is: Is it reliable to continue using FILE* after truncating the file? I mean, ANSI C FILE layer has its own buffer and does not know the file is truncated from beneath. In case the buffered bytes is beyond the truncated point, will the buffered content be flushed to the file when doing fclose() ?

If no guarantee, what is the best practice of using file I/O functions accompanied with truncate operation when write a Windows-Linux portable program?

Similar question: When querying file size from a file-handle returned by fileno , is it the accurate size when I later call fclose() -- without further fwrite()?

[EDIT 2012-12-11]

According to Joshua's suggestion. I conclude that current possible best practice is: Set the stream to unbuffered mode by calling setbuf(stream, NULL); , then truncate() or _chsize_s() can work peacefully with the stream.

Anyhow, no official document seems to explicitly confirm this behavior, whether Microsoft CRT or GNU glibc.

解决方案

The POSIX way....

ftruncate() is what you're looking for, and it's been in POSIX base specifications since 2001, so it should be in every modern POSIX-compatible system by now.

Note that ftruncate() operates on a POSIX file descriptor (despite its potentially misleading name), not a STDIO stream FILE handle. Note also that mixing operations on the STDIO stream and on the underlying OS calls which operate on the file descriptor for the open stream can confuse the internal runtime state of the STDIO library.

So, to use ftruncate() safely with STDIO it may be necessary to first flush any STDIO buffers (with fflush()) if your program may have already written to the stream in question. This will avoid STDIO trying to flush the otherwise unwritten buffer to the file after the truncation has been done.

You can then use fileno() on the STDIO stream's FILE handle to find the underlying file descriptor for the open STDIO stream, and you would then use that file descriptor with ftruncate(). You might consider putting the call to fileno() right in the parameter list for the ftruncate() call so that you don't keep the file descriptor around and accidentally use it yet other ways which might further confuse the internal state of STDIO. Perhaps like this (say to truncate a file to the current STDIO stream offset):

/*
 * 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 */

Note also that the POSIX definition of ftruncate() says "The value of the seek pointer shall not be modified by a call to ftruncate()", so this means you may also need to use use fseek() to set the STDIO layer (and thus indirectly the file descriptor) either to the new end of the file, or perhaps back to the beginning of the file, or somewhere still within the boundaries of the file, as desired. (Note that the fseek() should not be necessary if the truncation point is found using ftello().)

You should not have to make the STDIO stream unbuffered if you follow the procedure above, though of course doing so could be an alternative to using fflush() (but not fseek()).

Without POSIX....

If you need to stick to strict ISO Standard C, say C99, then you have no portable way to truncate a file to a given length other than zero (0) length. The latest draft of C11 that I have says this in Section 7.21.3 (paragraph 2):

Binary files are not truncated, except as defined in 7.21.5.3. Whether a write on a text stream causes the associated file to be truncated beyond that point is implementation-defined.

(and 7.21.5.3 describes the flags to fopen() which allow a file to be truncated to a length of zero)

The caveat about text files is there because on silly systems that have both text and binary files (as opposed to just plain POSIX-style content agnostic files) then it is often possible to write a value to the file which will be stored in the file at the position written and which will be treated as an EOF indicator when the file is next read.

Other types of systems may have different underlying file I/O interfaces that are not compatible with POSIX while still providing a compatible ISO C STDIO library. In theory if such a system offers something similar to fileno() and ftrunctate() then a similar procedure could be used with them as well, provided that one took the same care to avoid confusing the internal runtime state of the STDIO library.

With regard to querying file size....

You also asked whether the file size found by querying the file descriptor returned by fileno() would be an accurate representation of the file size after a successful call to fclose(), even without any further calls to fwrite().

The answer is: Don't do that!

As I mentioned above, the POSIX file descriptor for a file opened as a STDIO stream must be used very carefully if you don't want to confuse the internal runtime state of the STDIO library. We can add here that it is important not to confuse yourself with it either.

The most correct way to find the current size of a file opened as a STDIO stream is to seek to the end of it and then ask where the stream pointer is by using only STDIO functions.

这篇关于有一个保证和安全的方法来截断从ANSI C FILE指针的文件?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆