CODE: [Copy to clipboard]
/* fp是已经成功打开的可读文件指针 */
while (!feof (fp))
{
/* 调用fgets()或fscanf()等函数读取文件内容 */
/* 可以用printf()输出读取的内容,看看是否正确 */
}
这段代码的错误在于误解了文件末尾(End-of-file,EOF)的含义和feof()函数的功能。所谓文件末尾,往往被理解为文件的最后一个字符是EOF,即–1,而feof()读到这个EOF就返回一个非0值,表示文件读完了。while (!feof (fp))
{
/* 调用fgets()或fscanf()等函数读取文件内容 */
/* 可以用printf()输出读取的内容,看看是否正确 */
}
然而,这是完全错误的!
不论是文本还是二进制文件,其末尾并不存在这么一个EOF字符;
feof()本身也并不具备“读”的功能,它返回0还是非0,依据的是最近一次的读取操作,如fgets()、fscanf()或fread()等函数,是否造成“错误(Error)”,因此feof()永远不可能“读”到一个EOF。
判断文件结束的正确机制应该是这样的:那些具有读取功能的函数,在读入文件的最后一个字节(Last Byte)之后,如果不再继续读,那么程序无从判断文件是否结束;只有在读入最后一个字节之后再继续试图读取时,管理磁盘文件的操作系统才会设置一个非0的错误码(Error Code)。而ferror()、feof()等函数是依据这个错误码来判断文件操作方面出现了什么错误以及文件是否结束。无论是文件操作、内存分配还是网络通信,这种判定错误的机制是非常普遍的。
因此,按照上面的代码,当fgets()或fscanf()读到文本文件的最后一行时,并没有造成错误;此时回到循环判断语句,feof()自然返回0,继续执行读取,而这次读取才会触发错误,但fgets()或fscanf()上次所读取的变量并未被更新,因此最后一行的内容会再次被输出;然后,回到循环判断,feof()这时才返回非0,退出循环。至于那种要求文本文件末尾加一个空行的做法,只不过是把空行读了两遍而已。
其实,当fgets()返回NULL或fscanf()返回EOF时,都表示读取出错;在这种情况下,才需要用feof()或ferror()指示究竟是什么错误。所以,正确的文件读取循环应该是:
CODE: [Copy to clipboard]
/* fp是已经成功打开的可读文件指针 */
while (fgets(…))
/* 或 while (fscanf(fp, …)!=EOF)*/
{
…
}
/* printf(“%d %s\n”, feof(fp), ferror(fp)); 输出错误情况 */
while (fgets(…))
/* 或 while (fscanf(fp, …)!=EOF)*/
{
…
}
/* printf(“%d %s\n”, feof(fp), ferror(fp)); 输出错误情况 */
虽然上面讨论的是文本文件,对于二进制文件来说道理是一样的。至于带有DTD的标记语言,基本上都是调用剖析程序进行;而数据库相关的操作,基本上都是通过数据库接口函数进行的,这里就不再介绍了。
转自:
小木虫论坛的经典文章(3)---谈谈EOF与feof()
http://chenctxxt.blog.163.com/blog/static/21095800320123150456364/