| 查看: 487 | 回复: 2 | ||
[资源]
【原创】谈谈EOF与feof()已有2人参与
|
|
看到tjyl专家顾问的帖子《C语言中文件读取的一个小技巧 》(http://muchong.com/bbs/viewthread.php?tid=1990840),里面仍然在用feof()。 今天,就谈谈这个feof()咯。 很多程序在读取文本文件时,经常会出现一个有趣的现象:程序会把文件的最后一行读两遍。因此很多软件都要求文本文件要在最后添加一个空行。造成这种尴尬的原因主要在于编程时对于如何判断文件末尾产生了误解。 以C语言为例,很多教材、参考书等都给出类似下面的代码来解释如何判断文件结束: 这段代码的错误在于误解了文件末尾(End-of-file,EOF)的含义和feof()函数的功能。所谓文件末尾,往往被理解为文件的最后一个字符是EOF,即–1,而feof()读到这个EOF就返回一个非0值,表示文件读完了。 然而,这是完全错误的! 不论是文本还是二进制文件,其末尾并不存在这么一个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()指示究竟是什么错误。所以,正确的文件读取循环应该是: 虽然上面讨论的是文本文件,对于二进制文件来说道理是一样的。至于带有DTD的标记语言,基本上都是调用剖析程序进行;而数据库相关的操作,基本上都是通过数据库接口函数进行的,这里就不再介绍了。 [ Last edited by yalefield on 2010-4-24 at 10:31 ] |
» 猜你喜欢
请问有评职称,把科研教学业绩算分排序的高校吗
已经有6人回复
2025冷门绝学什么时候出结果
已经有6人回复
Bioresource Technology期刊,第一次返修的时候被退回好几次了
已经有7人回复
真诚求助:手里的省社科项目结项要求主持人一篇中文核心,有什么渠道能发核心吗
已经有8人回复
寻求一种能扛住强氧化性腐蚀性的容器密封件
已经有5人回复
请问哪里可以有青B申请的本子可以借鉴一下。
已经有4人回复
请问下大家为什么这个铃木偶联几乎不反应呢
已经有5人回复
天津工业大学郑柳春团队欢迎化学化工、高分子化学或有机合成方向的博士生和硕士生加入
已经有4人回复
康复大学泰山学者周祺惠团队招收博士研究生
已经有6人回复
AI论文写作工具:是科研加速器还是学术作弊器?
已经有3人回复
» 本主题相关价值贴推荐,对您同样有帮助:
Fortran有EOF文件结尾么?
已经有7人回复
【求助】EOF请教
已经有7人回复
【讨论】谈谈氧逸度在岩浆中的作用【已搜无重复】
已经有13人回复
★ ★ ★
小木虫(金币+0.5):给个红包,谢谢回帖交流
resonant(金币+2):分享,进步:-) 2010-04-24 12:41
小木虫(金币+0.5):给个红包,谢谢回帖交流
resonant(金币+2):分享,进步:-) 2010-04-24 12:41
|
学习了,写的非常好的。 对于fgets,gets在出错和到达文件末尾都是返回NULL, 而scanf,fscanf,sscanf在出错和到达文件尾都是返回EOF,用feof只是为了知道到底是不是因为到了文件末尾. FILE其实是一个结构体,里面包含里文件描述符、缓冲区指针、缓冲区长度、缓冲区内当前字符数和出错标准等等。 feof的实现有的是用宏,其实就是去检查FILE结构里的_flags字段。 ================================ struct _IO_FILE { int _flags; /* High-order word is _IO_MAGIC; rest is flags. */ #define _IO_file_flags _flags /* The following pointers correspond to the C++ streambuf protocol. */ /* Note: Tk uses the _IO_read_ptr and _IO_read_end fields directly. */ char* _IO_read_ptr; /* Current read pointer */ char* _IO_read_end; /* End of get area. */ char* _IO_read_base; /* Start of putback+get area. */ char* _IO_write_base; /* Start of put area. */ char* _IO_write_ptr; /* Current put pointer. */ char* _IO_write_end; /* End of put area. */ char* _IO_buf_base; /* Start of reserve area. */ char* _IO_buf_end; /* End of reserve area. */ /* The following fields are used to support backing up and undo. */ char *_IO_save_base; /* Pointer to start of non-current get area. */ char *_IO_backup_base; /* Pointer to first valid character of backup area */ char *_IO_save_end; /* Pointer to end of non-current get area. */ struct _IO_marker *_markers; struct _IO_FILE *_chain; int _fileno; int _blksize; _IO_off_t _old_offset; /* This used to be _offset but it's too small. */ #define __HAVE_COLUMN /* temporary */ /* 1+column number of pbase(); 0 is unknown. */ unsigned short _cur_column; signed char _vtable_offset; char _shortbuf[1]; /* char* _save_gptr; char* _save_egptr; */ _IO_lock_t *_lock; #ifdef _IO_USE_OLD_IO_FILE }; ================================== #define __sfeof(p) (((p)->_flags & __SEOF) != 0) #define __sferror(p) (((p)->_flags & __SERR) != 0) #define __sclearerr(p) ((void)((p)->_flags &= ~(__SERR|__SEOF))) #define __sfileno(p) ((p)->_file) #ifndef lint #ifndef _REENTRANT #define feof(p) __sfeof(p) #define ferror(p) __sferror(p) #define clearerr(p) __sclearerr(p) 没有摘抄完 ======================== 在同时读进去再写出来的时候 其实这样就可以了 fscanf(....) or fgets(...) while ( ! feof( fp ) ) { printf(...) or fputs.... fscanf(....) or fgets(...) } |
2楼2010-04-24 12:34:22
3楼2010-04-24 19:26:44













回复此楼