求助-读取文件出错,最后一行重复输出[已解决-都是feof惹的祸]
时间:2008-01-16 11:14:27 来源: 作者:
|
写了一个程序,可以格式化的从文件中读出自己想要的字段,然后分别显示在屏幕上,虽然方法可能傻了点,不过还算好用,后来在测试的时候,突然发现了一个问题,不知道为什么,当文件读到最后时,并没有结束,而是将最后一行字串又打了一遍,我试过将fgets换为fwrite,虽然不会再多打出一行,但是因为文件的长度不确定,结果输出的都是乱码,看了看书上的例子,好象只有定义结构体之后格式化输出才能正确读取,难道没有别的办法了么.... __________________________________________________________________ 运行环境: FC6/gcc4.0.1 程序如下: #include <stdio.h> #include <string.h> void file_format_read(void) { FILE *filep; char read_log[100]=" "; char select_file_name[20]=" "; char select_file_date[20]=" "; char select_file_length[20]=" "; char select_file_size[20]=" "; filep=fopen("file_format.txt","r"); if(!filep) printf("open file error!n"); printf("open file ok!n"); while(!feof(filep)) { fgets(read_log,100,filep); strncpy(select_file_name,read_log,16); strncpy(select_file_date,read_log+17,16); strncpy(select_file_length,read_log+34,strcspn(read_log,"#")-strcspn(read_log,"@")-1); strncpy(select_file_size,strchr(read_log,'#')+1,strcspn(read_log,"$")-strcspn(read_log,"#")-1); printf("fread = %sn",read_log); printf("select_file_name = %sn",select_file_name); printf("select_file_date = %sn",select_file_date); printf("select_file_length = %sn",select_file_length); printf("select_file_size = %sn",select_file_size); printf("**********************************n"); strcpy(select_file_name," "); strcpy(select_file_date," "); strcpy(select_file_length," "); strcpy(select_file_size," "); } fclose(filep); } int main() { file_format_read(); return 1; } 用来读的文件: 名称:file_format.txt 内容: 200706201421.avi 2007-06-20 14:21@58分钟#45M% 200706201422.avi 2007-06-20 14:22@158分钟#45M% 200706201423.avi 2007-06-20 14:23@58分钟#145M% 200706201424.avi 2007-06-20 14:24@58分钟#45M% 200706201425.avi 2007-06-20 14:25@258分钟#245M% 200706201426.avi 2007-06-20 14:26@58分钟#45M% 200706201427.avi 2007-06-20 14:27@58分钟#45M% 200706201428.avi 2007-06-20 14:28@2258分钟#2145M% 200706201429.avi 2007-06-20 14:29@58分钟#45M% [ 本帖最后由 封神 于 2007-7-3 09:10 编辑 ] 福瑞哈哥 回复于:2007-07-02 20:45:53 记得这种把feof当作循环测试条件,造成最后一行打印两次的问题在论坛已经多次出现了。 一句话:忘掉feof,直接用fgets。 JohnBull 回复于:2007-07-02 20:48:47 可以用feof,但必须记住:当文件刚刚读到文件尾时,feof不会返回true。只有在文件尾部再次进行一次读操作,feof才会返回真。 这与底层系统调用相关。因为只有read返回一次0值,进程才能知道文件结束了。 MMMIX 回复于:2007-07-02 21:50:58 引用:原帖由 福瑞哈哥 于 2007-7-2 20:45 发表 记得这种把feof当作循环测试条件,造成最后一行打印两次的问题在论坛已经多次出现了。 一句话:忘掉feof,直接用fgets。 呵呵,看到标题我就猜到是 feof 惹的祸,一看果然如此。 flw 回复于:2007-07-02 21:54:44 feof() 就是蛇足,见一次说一次。 MMMIX 回复于:2007-07-02 21:57:04 引用:原帖由 flw 于 2007-7-2 21:54 发表 feof() 就是蛇足,见一次说一次。 果然又是这话 :em06::em06::em06: 封神 回复于:2007-07-02 23:17:12 受教了,那么将判断条件改为 while(fgets(read_log,100,filep)) 就可以了么? 家里没有编译器,明天到公司试试. 封神 回复于:2007-07-02 23:19:34 话说回来,既然这个函数有漏洞,为什么新版本的GCC没有修正这个错误呢,反尔将很多有用字符串处理函数去掉了不少. MMMIX 回复于:2007-07-02 23:26:25 引用:原帖由 封神 于 2007-7-2 23:19 发表 话说回来,既然这个函数有漏洞,为什么新版本的GCC没有修正这个错误呢,反尔将很多有用字符串处理函数去掉了不少. 谁说这个是错误/漏洞? 封神 回复于:2007-07-03 08:38:54 这个不算是漏洞么? ypxing 回复于:2007-07-03 08:44:26 这个不是漏洞, 只是多少有些容易产生误导 引用:原帖由 封神 于 2007-7-3 08:38 发表 这个不算是漏洞么? ivhb 回复于:2007-07-03 08:52:29 引用:原帖由 封神 于 2007-7-2 20:25 发表 写了一个程序,可以格式化的从文件中读出自己想要的字段,然后分别显示在屏幕上,虽然方法可能傻了点,不过还算好用,后来在测试的时候,突然发现了一个问题,不知道为什么,当文件读到最后时,并没有结束,而是将最后一行字 ... while (!feof(fp)) { buf[0] = 0x00; fgets(buf, sizeof buf, fp); if (buf[0] == 0x00) break; // ferror(fp) 或者 feof(fp) ... } if (ferror(fp)) { // 对应break出错时候蹦出来的情况 } 当然,还是像他们说的一样,直接fgets放在while 里最好了,没有必要的。 [ 本帖最后由 ivhb 于 2007-7-3 08:53 编辑 ] 封神 回复于:2007-07-03 09:09:08 收到,谢了 福瑞哈哥 回复于:2007-07-03 09:28:15 引用:原帖由 ivhb 于 2007-7-3 08:52 发表 while (!feof(fp)) { buf[0] = 0x00; fgets(buf, sizeof buf, fp); if (buf[0] == 0x00) break; // ferror(fp) 或者 feof(fp) ... } if (ferror(fp)) { // 对应break出错时候蹦出来的情 ... 没有测试过你的程序,但是感觉有问题。 MMMIX 回复于:2007-07-03 09:38:49 引用:原帖由 封神 于 2007-7-3 08:38 发表 这个不算是漏洞么? 也许在设计之初算是,但是现在这个已经写入标准,那么就不再是 bug,而是标准了 :mrgreen: ivhb 回复于:2007-07-03 12:25:41 引用:原帖由 福瑞哈哥 于 2007-7-3 09:28 发表 没有测试过你的程序,但是感觉有问题。 :) 如果是磁盘文件来说,这么做不会有什么问题。 因为磁盘文件,ferror几乎不可能为错。只要你打开成功的话。 对于ferror来说,因为没有规定 fgets失败,buf是否会被复写。因此这个是未定义的。 但是到达文件结束,buf会left unchanged。 因此,这么做几乎是等价的吧。 封神 回复于:2007-07-03 15:06:53 最终选择在while中写fgets,对于我个人来说比较好理解,再次谢谢大家。 引用:原帖由 MMMIX 于 2007-7-3 09:38 发表 也许在设计之初算是,但是现在这个已经写入标准,那么就不再是 bug,而是标准了 :mrgreen: 强权标准啊,根本就不好用,还要强迫大家接受,修正这么一个函数又不会死人,真不知道为什么那些家伙不改 思一克 回复于:2007-07-03 16:10:27 LZ,你应该判断fgets()为NULL,不判断而继续使用buf的内容,自己的程序是不对的。 foef(stream)针对的是”流“, 用一个indicator(指示)表明文件尾,而feof判断这个指示是否设置,而不是判断当前SEEK数值是否到尾。 seek值到尾了对于“流”不一定是是文件尾,因为“流”是会不断增长的。比如pipe, sockfd等。 不是BUG,不要想象这么多年了编制库函数的人都愚不可及。 NAME clearerr, feof, ferror, fileno - check and reset stream status SYNOPSIS #include <stdio.h> void clearerr(FILE *stream); int feof(FILE *stream); int ferror(FILE *stream); int fileno(FILE *stream); DESCRIPTION The function clearerr clears the end-of-file and error indicators for the stream pointed to by stream. The function feof tests the end-of-file indicator for the stream pointed to by stream, returning non-zero if it is set. The end-of-file indicator can only be cleared by the function clearerr. The function ferror tests the error indicator for the stream pointed to by stream, returning non-zero if it is set. The error indicator can only be reset by the clearerr function. The function fileno examines the argument stream and returns its integer descriptor. For non-locking counterparts, see unlocked_stdio(3). ERRORS These functions should not fail and do not set the external variable errno. (However, in case fileno detects that its argument is not a valid stream, it must return -1 and set errno to EBADF.) CONFORMING TO The functions clearerr, feof, and ferror conform to X3.159-1989 (????€?ANSI C????€?. SEE ALSO open(2), unlocked_stdio(3), stdio(3) ivhb 回复于:2007-07-03 16:48:44 引用:原帖由 思一克 于 2007-7-3 16:10 发表 LZ,你应该判断fgets()为NULL,不判断而继续使用buf的内容,自己的程序是不对的。 foef(stream)针对的是”流“, 用一个indicator(指示)表明文件尾,而feof判断这个指示是否设置,而不是判断当前SEEK数值是否 ... 一直很欣赏 思一克, 不会放过任何一个细小的细节。 见你说了这么多,忍不住顺便也说一下,对于pipe,sock,本身是不可seek的,这是其一; 另外,如果当前的pipe/sock里没有可读的数据,fgets会一直等待,根本不会返回:),因此 也不会给判断buf的机会:) 除非pipe/sock另外一写入端close或者读到足够的字节,或者被信号打断. 如果你能进入到判定buf的时候,一定是1. 读到足够的字节数,2不足,但是已经到文件尾。 如果buf[0] == 0x00, 那么,一定是到了文件尾:) 考虑到低速设备容易被信号打断,因此,一上来就用了,如果对于磁盘文件 ... [ 本帖最后由 ivhb 于 2007-7-3 16:58 编辑 ] 思一克 回复于:2007-07-03 17:02:40 可能我用直接用sock, pipe做例子不对。 但可以想,LIB库,用一个指示来判断,应该有其道理的,虽然会引起误导。 根据是什么,就是这么成熟的使用多年的库,有是这么简单的问题,不可能有错误。而且man page中说的也是那样判断eof的指示是否设置,而不是判断目前是否读在最后一个字节。至于指示怎么被设置没有说。 再有,且不说以上的,就是程序中判断错误,也应该用最后那个操作fgets()来判断,否则直接fgets后就利用buf内容是绝对不对的,因为fgets返回错误的情况呢? 引用:原帖由 ivhb 于 2007-7-3 16:48 发表 一直很欣赏 思一克, 不会放过任何一个细小的细节。 见你说了这么多,忍不住顺便也说一下,对于pipe,sock,本身是不可seek的,这是其一; 另外,如果当前的pipe/sock里没有可读的数据,fgets会一直等待, ... 福瑞哈哥 回复于:2007-07-03 17:11:00 引用:原帖由 ivhb 于 2007-7-3 08:52 发表 while (!feof(fp)) { buf[0] = 0x00; fgets(buf, sizeof buf, fp); if (buf[0] == 0x00) break; // ferror(fp) 或者 feof(fp) ... } if (ferror(fp)) { // 对应break出错时候蹦出来的情 ... 是的,你的程序没问题。我上午只看到了if(buf[0]==0x00)这句,没看到你上面buf[0]==0x00这句,觉得这样写程序很没有必要。fgets有返回值直接使用就可以了,逻辑越多别人越不容易理解。 MMMIX 回复于:2007-07-03 19:01:39 引用:原帖由 封神 于 2007-7-3 15:06 发表 最终选择在while中写fgets,对于我个人来说比较好理解,再次谢谢大家。 强权标准啊,根本就不好用,还要强迫大家接受,修正这么一个函数又不会死人,真不知道为什么那些家伙不改 为了向后兼容。 whyglinux 回复于:2007-07-03 23:46:05 >> 都是feof惹的祸 >> 这个不算是漏洞么? 是漏洞。只不过是你写的程序中存在的漏洞,并不是 feof 函数的过错。产生漏洞的原因是你还没有掌握 feof 函数的正确使用方法,而不是什么“都是feof惹的祸”。 >> 强权标准啊,根本就不好用,还要强迫大家接受,修正这么一个函数又不会死人,真不知道为什么那些家伙不改 首先要想清楚你想要他们改什么,怎么改? feof 函数的声明和行为已经是包括 ANSI C 标准在内的很多标准规定的函数了。即使是 C 标准委员会的人想对它进行修改也已经是不可能的事了,况且实际上也无需修改。 ================ 其实正确使用 feof 函数很容易,只要记住一个原则就是了:“先读文件后用 feof 判断”。你上面使用 feof 函数的程序都没有做到这一点,而是先用 feof 函数判断、然后再读文件,所以是不正确的。可根据上面的这个原则修改你的程序,使之正确。 思一克 回复于:2007-07-04 09:01:37 whyglinux说的准确。漏洞是LZ的程序,而不是feof. 引用:原帖由 whyglinux 于 2007-7-3 23:46 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=7001994&ptid=956786]
>> 都是feof惹的祸 >> 这个不算是漏洞么? 是漏洞。只不过是你写的程序中存在的漏洞,并不是 feof 函数的过错。产生漏洞的原因是你还没有掌握 feof 函数的正确使用方法,而不是什么“都是feof惹的祸”。 ... while(1) 回复于:2007-07-04 09:41:48 “先读文件后用 feof 判断” :funk: 原来如此 福瑞哈哥 回复于:2007-07-04 09:49:18 每次看到有人用feof我都会感到同情! 不管feof有没有用,如果使用fgets而不判断返回值都是错误! 如果判断了fgets的返回值,也就没有必要使用feof了。 引用:原帖由 while(1) 于 2007-7-4 09:41 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=7002848&ptid=956786]
“先读文件后用 feof 判断” :funk: 原来如此 ivhb 回复于:2007-07-04 10:26:33 引用:原帖由 while(1) 于 2007-7-4 09:41 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=7002848&ptid=956786]
“先读文件后用 feof 判断” :funk: 原来如此 假定进程A读取文件已经到了文件尾,feof测试为真。 如果此时进程B追加A正在读取的文件,你觉得,如果A不发生进一步的读/写动作, 接着测试feof,你觉得是真还是假? feof仅仅是个宏,仅仅读取了上次读/写动作发生后置于FILE 结构的一个标志位。 只有进一次的读/写动作才会更新那个标志位。 思一克 回复于:2007-07-04 10:31:36 应该仍然是真。因为INDICATOR的缘故 我没有具体测试。 引用:原帖由 ivhb 于 2007-7-4 10:26 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=7003216&ptid=956786]
假定进程A读取文件已经到了文件尾,feof测试为真。 如果此时进程B追加A正在读取的文件,你觉得,如果A不发生进一步的读/写动作, 接着测试feof,你觉得是真还是假? feof仅仅是个宏,仅仅读取了上次 ... while(1) 回复于:2007-07-04 10:58:00 引用:原帖由 福瑞哈哥 于 2007-7-4 09:49 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=7002918&ptid=956786]
每次看到有人用feof我都会感到同情! 不管feof有没有用,如果使用fgets而不判断返回值都是错误! 如果判断了fgets的返回值,也就没有必要使用feof了。 fgets返回NULL的情况就表示一定是文件读完了么? 福瑞哈哥 回复于:2007-07-04 11:04:13 难道还有其他选择? fgets返回null表示这个文件对程序来说是读完了。基本上就是这样,有其他情况99%你也不会处理的。 引用:原帖由 while(1) 于 2007-7-4 10:58 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=7003404&ptid=956786]
fgets返回NULL的情况就表示一定是文件读完了么? while(1) 回复于:2007-07-04 11:04:44 引用:原帖由 ivhb 于 2007-7-4 10:26 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=7003216&ptid=956786]
假定进程A读取文件已经到了文件尾,feof测试为真。 如果此时进程B追加A正在读取的文件,你觉得,如果A不发生进一步的读/写动作, 接着测试feof,你觉得是真还是假? feof仅仅是个宏,仅仅读取了上次 ... 我的理解是:fgets读到文件最后一行的时候(返回不是NULL) ,feof测试为假,再fgets一次(返回为NULL),再feof测试为真。对否? 你的问题我觉得是 如果在A中用feof是真,在B中用feof是假,对否? while(1) 回复于:2007-07-04 11:06:51 引用:原帖由 福瑞哈哥 于 2007-7-4 11:04 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=7003435&ptid=956786]
难道还有其他选择? fgets返回null表示这个文件对程序来说是读完了。基本上就是这样,有其他情况99%你也不会处理的。 我的意思是fgets出错的情况有可能么?这时候fgets是什么反应?好奇 福瑞哈哥 回复于:2007-07-04 11:08:21 看man page, gets() and fgets() return s on success, and NULL on error or when end of file occurs while no characters have been read. 不要将问题考虑的那样复杂 理论上fgets确实有出错的可能,但是谁又会去处理它呢,又如何处理呢?至今阅码无数,尚无见识过处理fgets错误的代码。 引用:原帖由 while(1) 于 2007-7-4 11:06 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=7003452&ptid=956786]
我的意思是fgets出错的情况有可能么?这时候fgets是什么反应?好奇 [ 本帖最后由 思一克 于 2007-7-5 15:20 编辑 ] MMMIX 回复于:2007-07-04 11:42:53 引用:原帖由 福瑞哈哥 于 2007-7-4 11:08 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=7003459&ptid=956786]
理论上fgets确实有出错的可能,但是谁又会去处理它呢,又如何处理呢?至今阅码无数,尚无见识过处理fgets错误的代码。 不会这么夸张吧?
这种例子一堆一堆的。 [ 本帖最后由 MMMIX 于 2007-7-4 11:44 编辑 ] 清汤挂面 回复于:2007-07-05 11:31:10 每次读之前memset一下buffer 福瑞哈哥 回复于:2007-07-05 11:32:43 引用:原帖由 MMMIX 于 2007-7-4 11:42 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=7003684&ptid=956786]
不会这么夸张吧? while(fgets(..., stream)){ /* ... */ } if(ferror(stream)){ /* ... */ } 这种例子一堆一堆的。 我是没见过。 if(ferror(stream)) {}里面写什么? xiaomiao 回复于:2007-07-05 11:46:05 不如用C++的ifstream + while循环 ifstream in("/tmp/test.txt",ios::in); string temp; while(getline(in,temp,'n')) { //..... } MMMIX 回复于:2007-07-05 12:24:07 引用:原帖由 福瑞哈哥 于 2007-7-5 11:32 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=7009049&ptid=956786]
我是没见过。 if(ferror(stream)) {}里面写什么? 来个完整的例子吧,APUE2 的 Figure 15.11
ivhb 回复于:2007-07-05 14:27:04 如果要提供一个函数给其他人用,ferror判断就有用了吧。 比如读取informix下unload出来的文件,如果行尾是表示连接下一行。 如果提供一个函数叫做
这样可以约定返回<0就是出错,>= 0 就是实际读取的行数,表示成功。 调用你函数的人就可以根据返回来决定采取什么动作了。 ..... 我们不讨论这么封装模块化的优劣,仅仅是个例子来说明一下而已。 [ 本帖最后由 ivhb 于 2007-7-5 14:30 编辑 ] 思一克 回复于:2007-07-05 15:22:24 gets() and fgets() return s on success, and NULL on error or when end of file occurs while no characters have been read. fgets错误或EOF都返回NULL 福瑞哈哥 回复于:2007-07-05 15:48:01 引用:原帖由 思一克 于 2007-7-5 15:22 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=7010802&ptid=956786]
gets() and fgets() return s on success, and NULL on error or when end of file occurs while no characters have been read. fgets错误或EOF都返回NULL 是的,fgets在eof和出错时都会返回NULL,只是一般情况下可以忽略错误。当然,可以更严谨一些。 封神 回复于:2007-07-09 09:45:33 汗,没想到已经讨论到这么高深的地步了,太复杂了,我还是只记住while(fgets(...))就好了 思一克 回复于:2007-07-09 10:36:48 其实也没有什么高深的。就是你判断fgets是否真返回了内容就可以了。 还有,CLIB库已经非常成熟,一旦出莫名其妙的问题,先研究自己程序那里有问题了,而不是怀疑系统BUG。 如果细心看MAN PAGE,一切解释都有。 比如,putenv()的参数问题,我也莫名其妙过,但一看man page,就不再上当了。 引用:原帖由 封神 于 2007-7-9 09:45 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=7024443&ptid=956786]
汗,没想到已经讨论到这么高深的地步了,太复杂了,我还是只记住while(fgets(...))就好了 ivhb 回复于:2007-07-09 10:46:17 呵呵,手册页真是好东西,对于可能出现的错误都会提醒你,而不是藏着掖着。 封神 回复于:2007-07-10 10:22:35 恩恩,收到了.谢谢大家的指点. PS:因为E文比较差,看E文资料十分痛苦,哎,什么时候汉语成为世界通用语就好了(无聊的YY中)......... |
原文链接:http://bbs.chinaunix.net/viewthread.php?tid=956786 转载请注明作者名及原文出处 |










文章评论
共有 位网友发表了评论 查看完整内容