feof()在C中是如何工作的
feof()是否为filepointer的当前位置检查eof或检查当前filepointer旁边的位置?
谢谢你的帮助 !
每个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
这就是为什么以下是在阅读文件时使用eof的错误方法:
f = fopen(filename, "r"); while (!feof(f)) { c = getc(f); putchar(c); }
由于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"); }
对于错误情况, FILE
对象还有另一个内部标志: ferror
。 测试错误通常更清楚,而不是“不是文件结束”。 在C中读取文件的惯用方法是这样的:
f = fopen(filename, "r"); while ((c = getc(f)) != EOF) { putchar(c); } if (ferror(f)) { perror(filename): exit(EXIT_FAILURE); } fclose(f);
(为简洁起见,此处的示例省略了一些错误检查。)
feof
函数很少有用。
通过了解feof
的实现方式,您可以更好地了解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);
}
stdio库为每个文件维护一个包含_base
指向的内部缓冲区的结构。 缓冲区中的当前字符由_ptr
指向,可用字符数包含在_cnt
。 getc
宏是很多高级function的基础,比如scanf
,它试图从缓冲区返回一个字符。 如果缓冲区为空,它将调用_filbuf
来填充它。 _filbuf
反过来会调用read
。 如果read
返回0,这意味着没有更多数据可用, _filbuf
将设置_IOEOF
标志,每次调用它时feof
检查返回true。
正如您从上面可以理解的那样,当您第一次尝试读取文件末尾的字符(或者库函数代表您尝试)时, feof
将返回true。 这对各种function的行为有微妙的影响。 考虑一个包含单个字符的文件:数字1
。 用getc
读取该字符后, feof
将返回false,因为_IOEOF
标志未设置; 没有人试图读过文件的结尾。 再次调用getc
将导致调用read
, _IOEOF
标志的设置,这将导致feof
返回true。 但是,在使用fscanf("%d", &n)
从同一文件中读取数字后, feof
将立即返回true,因为fscanf
将尝试读取整数的其他数字。