feof()在C中是如何工作的

Alf*_*mes 11 c file-handling feof

feof()是否为filepointer的当前位置检查eof或检查当前filepointer旁边的位置?

谢谢你的帮助 !

小智 32

每个FILE流都有一个内部标志,指示调用者是否已尝试读取文件末尾.feof返回那个标志.该标志不指示当前文件位置是否作为文件的结尾,仅指示先前的读取是否已尝试读取超过文件的末尾.

作为一个例子,让我们一起阅读包含两个字节的文件.

f = fopen(filename, "r"); // file is opened
assert(!feof(f));         // eof flag is not set
c1 = getc(f);             // read first byte, one byte remaining
assert(!feof(f));         // eof flag is not set
c2 = getc(f);             // read second byte, no bytes remaining
assert(!feof(f));         // eof flag is not set
c3 = getc(f);             // try to read past end of the file
assert(feof(f));          // now, eof flag is set
Run Code Online (Sandbox Code Playgroud)

这就是为什么以下是在阅读文件时使用eof 的错误方法:

f = fopen(filename, "r");
while (!feof(f)) {
    c = getc(f);
    putchar(c);
}
Run Code Online (Sandbox Code Playgroud)

由于方式feof有效,文件结束标志仅在getc 尝试读取超过文件末尾时设置.getc然后将返回EOF,这不是一个字符,并且循环结构导致putchar尝试将其写出,导致错误或垃圾输出.

每个C标准库输入方法都会返回成功或失败的指示:如果它尝试读取文件末尾或者读取时出错,则getc返回特殊值EOF.对于文件结束和错误,特殊值是相同的,这是使用正确方法的地方 feof:您可以使用它来区分文件结束和错误情况.

f = fopen(filename, "r");
c = getc(f);
if (c == EOF) {
    if (feof(f))
        printf("it was end-of-file\n");
    else
        printf("it was error\n");
}
Run Code Online (Sandbox Code Playgroud)

FILE错误情况的对象还有另一个内部标志: ferror.测试错误通常更清楚,而不是"不是文件结束".在C中读取文件的惯用方法是这样的:

f = fopen(filename, "r");
while ((c = getc(f)) != EOF) {
    putchar(c);
}
if (ferror(f)) {
    perror(filename):
    exit(EXIT_FAILURE);
}
fclose(f);
Run Code Online (Sandbox Code Playgroud)

(为简洁起见,此处的示例省略了一些错误检查.)

feof功能很少有用.


Dio*_*lis 5

feof通过了解它是如何实现的,您可以更好地了解它的工作原理。这是第 7 版 Unix stdio 库如何实现的简化版本feof。现代库非常相似,添加了提供线程安全、提高效率和更清晰实现的代码。

extern  struct  _iobuf {
    char    *_ptr;
    int     _cnt;
    char    *_base;
    char    _flag;
    char    _file;
} _iob[_NFILE];

#define _IOEOF  020

#define feof(p)         (((p)->_flag&_IOEOF)!=0)

#define getc(p)         (--(p)->_cnt>=0? *(p)->_ptr++&0377:_filbuf(p))

int
_filbuf(FILE *iop)
{

    iop->_ptr = iop->_base;
    iop->_cnt = read(fileno(iop), iop->_ptr, BUFSIZ);
    if (iop->_cnt == 0) {
            iop->_flag |= _IOEOF;
            return(EOF);
    }
    return(*iop->_ptr++ & 0377);
Run Code Online (Sandbox Code Playgroud)

}

stdio 库为每个文件维护一个包含由 指向的内部缓冲区的结构_base。缓冲区中的当前字符由 指向,_ptr可用字符数包含在 中_cnt。的getc宏,这是很多的更高级别的功能的基础上,像scanf,尝试从缓冲器返回一个字符。如果缓冲区为空,它将调用_filbuf填充它。 _filbuf反过来会打电话read。如果read返回 0,表示没有更多的数据可用,_filbuf将设置_IOEOF标志,即feof您每次调用它时检查以返回 true。

从上面可以理解,当您feof第一次尝试读取超过文件末尾的字符(或库函数代表您尝试)时,将返回 true。这对各种函数的行为有微妙的影响。考虑一个包含单个字符的文件:数字1。使用 读取该字符后getcfeof将返回 false,因为该_IOEOF标志未设置;还没有人尝试读取文件末尾。getc再次调用将导致read_IOEOF标志设置的调用,这将导致feof返回 true。但是,在使用 读取同一文件中的数字后fscanf("%d", &n)feof将立即返回 true,因为fscanf 将尝试读取整数的其他数字。