C语言文件补充笔记1:EOF与feof
创始人
2024-05-12 03:59:15
0

1 关于EOF

可以查看EOF的宏定义
在这里插入图片描述
函数fgetc如果读取失败就返回-1,对于文本文件而言,以为着读取结束,因此-1可以作为结束的标志。

#define _CRT_SECURE_NO_WARNINGS
#include
int main() {FILE* fp = fopen("a.txt", "r");if (NULL == fp){perror("open");	return -1;		//在main函数中,按照程序开发的一般惯例,成功则返回0,失败则返回-1}char ch;while ((ch = fgetc(fp))!=-1){printf("%c", ch);}printf("\n");return 0;
}

刚刚查看宏定义可知EOF就是-1,因此上面的循环判断可以用EOF替代
这里需要注意的一点是,可以用fgetc的返回值是否为-1来判断文件是否读取结束,但并不意味着文件最后一个字符为-1

#define _CRT_SECURE_NO_WARNINGS
#include
int main() {FILE* fp = fopen("a.txt", "r");if (NULL == fp){perror("open");	return -1;		//在main函数中,按照程序开发的一般惯例,成功则返回0,失败则返回-1}char ch;while ((ch = fgetc(fp))!=EOF)	//这里原来是-1,现在改成了EOF{printf("%c", ch);}printf("\n");return 0;
}

2 文件中包含二进制数字

假如-1(EOF对应数字)被写进了文件中,那么读取的时候,也会被正常读出来,如果仍然用fputc的返回值是否为-1来判断文件是否读取结束,那么就会出现bug。
例如:

#define _CRT_SECURE_NO_WARNINGS
#include
int main() {FILE* fp = fopen("a.txt", "w");if (NULL == fp){perror("open");	return -1;		//在main函数中,按照程序开发的一般惯例,成功则返回0,失败则返回-1}char a[10] = { 97, 98, 99, -1, -2, 100 };int i = 0;while (a[i]!=0){fputc(a[i], fp);i++;}return 0;
}

上面的程序是把字符数组写入文件,字符数组a中,有一个元素是-1,写入之后,打开a.txt文件,内容如下:
在这里插入图片描述
-1和-2无法根据ASCII码确定字符,因此乱码
我们再以EOF作为文件结束标志,读取文件

#define _CRT_SECURE_NO_WARNINGS
#include
int main() {FILE* fp = fopen("a.txt", "r");char ch;while ((ch = fgetc(fp)) != EOF) printf("%c", ch);return 0;
}

输出

abc

也就是说,当读到-1的时候,就退出了while循环,后面的不会正常打印

3 feof函数

为了避免上述问题的产生,可以使用 feof函数

#define _CRT_SECURE_NO_WARNINGS
#include
int main() {FILE* fp = fopen("a.txt", "r");char ch;while (!feof(fp)){ch = fgetc(fp);printf("%c", ch);	//feof(fp)返回0表示没有到文件末尾,继续读}printf("\n");return 0;
}

在这里插入图片描述

5 一个程序中多个文件指针打开同一个文件

上面的程序,假如写和读在同一个源程序中,但写指针和都指针使用不同的名字,程序如下:

#define _CRT_SECURE_NO_WARNINGS
#include
int main() {FILE* fp1 = fopen("a.txt", "w");if (NULL == fp1){perror("open");	return -1;		//在main函数中,按照程序开发的一般惯例,成功则返回0,失败则返回-1}char a[10] = { 97, 98, 99, -1, -2, 100 };int i = 0;while (a[i]!=0){fputc(a[i], fp1);i++;}FILE* fp2 = fopen("a.txt", "r");char ch;while ((ch = fgetc(fp2)) != EOF) printf("%d", ch);printf("  %d\n", ch);return 0;
}

输出

  -1

查看a.txt文件,可以发现成功将97, 98, 99, -1, -2, 100对应的字符写入,但后面却无法通过fp2读取,并且ch取到的只有-1,即读取失败。具体原因我不知道,但一个程序中,不能出现多个文件指针指向同一个文件

5 文件复制

#define _CRT_SECURE_NO_WARNINGS
#includeint main() {FILE* fp1 = fopen("a.txt", "r");if (NULL == fp1){perror("open");return -1;		//在main函数中,按照程序开发的一般惯例,成功则返回0,失败则返回-1}FILE* fp2 = fopen("b.txt", "w");if (NULL == fp2){perror("open");return -1;		//在main函数中,按照程序开发的一般惯例,成功则返回0,失败则返回-1}char ch;while (!feof(fp1)){ch = fgetc(fp1);fputc(ch, fp2);printf("%d\t", ch);}fclose(fp1);fclose(fp2);return 0;
}

输出:

97      98      99      -1      -2      100     -1

文件结束符-1也会被写进去

如果不想写进去那么必须在feof函数调用之前先读一次,如:

#define _CRT_SECURE_NO_WARNINGS
#includeint main() {FILE* fp1 = fopen("a.txt", "r");if (NULL == fp1){perror("open");return -1;		//在main函数中,按照程序开发的一般惯例,成功则返回0,失败则返回-1}FILE* fp2 = fopen("b.txt", "w");if (NULL == fp2){perror("open");return -1;		//在main函数中,按照程序开发的一般惯例,成功则返回0,失败则返回-1}char ch;while (1){ch = fgetc(fp1);if (feof(fp1))break;fputc(ch, fp2);printf("%d\t", ch);}fclose(fp1);fclose(fp2);return 0;
}

输出

97      98      99      -1      -2      100

上面两个程序的区别在于,前者是先判断后读,后者是先读后判断。

6 使用ftell验证feof的行为

为什么先读后判断,和先判断后读,会出现上面的差别?

好的,我们现在就来找原因
现有一个名为a.txt的文件,内容如下:
在这里插入图片描述
现在使用先判断后读的方式,查看光标的变化,设计如下程序

#define _CRT_SECURE_NO_WARNINGS
#includevoid test_feof1() {FILE* fp1 = fopen("a.txt", "r");char ch;long i = 0;while (!feof(fp1)){i = ftell(fp1);printf("%d\t", i);ch = fgetc(fp1);i = ftell(fp1);printf("%d\t", i);printf("%d\n", ch);}fclose(fp1);
}int main() {test_feof1();return 0;
}

输出

0       1       51
1       2       43
2       3       53
3       4       61
4       5       56
5       7       10
7       7       -1

最后读到的字符是\n(ASCII码为10),接下来进行了下一轮的循环,随后fgetc读取失败返回-1,当读取换行符\n后文件光标向后移动两格(后面会介绍),即从从位置5到位置7。读完\n后,feof仍然认为文件没有结束,否则最后一轮循环就不会执行,因此上面的猜测是错误的。

现在使用先读后判断的方式,查看光标的变化

void test_feof2() {FILE* fp1 = fopen("a.txt", "r");char ch;long i;while (1){i = ftell(fp1);printf("%d\t", i);ch = fgetc(fp1);i = ftell(fp1);printf("%d\t", i);if (feof(fp1))break;printf("%d\n", ch);}return 0;
}
int main() {//test_feof();test_feof2();return 0;
}

输出:

0       1       51
1       2       43
2       3       53
3       4       61
4       5       56
5       7       10
7       7

现在可以得到结论了:feof函数什么时候判断文件结尾,只有fputc读取失败(返回-1)后,这个函数才会判定文件结束,哪怕是读完最后一个字符也不行,必须fputc读取失败才行。

至此,先读后判断和先判断后读的差别,原因也找到了。

7 feof不要和fgets配合使用,建议只和fgetc配合使用

使用函数feof判断文件是否读取结束,与fgetc和fgets两种读取函数配合时的表现是不一样的。当feof与fgetc配合使用时,fgetc只有读取失败返回-1之后,feof才认为文件读取结束,而feof与fgets配合时,只需要最后一行读完,feof就认为文件读取结束,无需等到读取失败。

我们来看一下feof与fgets配合使用时的的情形。新建一个名为test_line.txt的文件,内容如下:
在这里插入图片描述
新建一个统计行数(文件有多少行)的函数,内容如下:

#define _CRT_SECURE_NO_WARNINGS
#includeint line_number1()
{FILE* fp = fopen("./test_line.txt", "r");if (!fp){perror("open");exit(0);}int i = 0;char temp[256];while (1){fgets(temp, sizeof(temp), fp);if (feof(fp)){fclose(fp);return i;}i++;}
}

main函数如下:

int main()
{int lines1;lines1 = line_number1();printf("lines1=%d\n", lines1);
}

输出

lines1=2

while循环只执行了两次,因为读完第三行之后,feof就认为文件读取结束,后面的i++没来得及执行,就退出了循环。
如果把i++移动到if语句前面,也是不行的,因为当文件是一个空文件时,i++也会执行,会造成即使是空文件,其行数也为1。

那么当使用fgets读取文件时,如何判定读取结束呢?答案是使用fgets的返回值,fgets读到文件尾或出错返回的是NULL,因此可以用返回值是否为NULL判定是否读取结束。代码如下:

int line_number2()
{FILE* fp = fopen("./test_line.txt", "r");if (!fp){perror("open");exit(0);}int i = 0;char temp[256];char* p = NULL;while (1){p = fgets(temp, sizeof(temp), fp);if (NULL == p){fclose(fp);return i;}i++;}
}

main函数如下:

int main()
{int lines1, lines2;lines1 = line_number1();//printf("lines1=%d\n", lines1);lines2 = line_number2();printf("lines1=%d  lines2=%d\n", lines1, lines2);}

输出

lines1=2  lines2=3

结果符合要求。

8 关于Windows中换行被当成两个字符的问题

现在还有一个疑问,为什么读到换行符时,光标会向后移动两格?
新建b.txt,内容如下:
在这里插入图片描述

void test_n() {FILE* fp1 = fopen("b.txt", "r");char ch;long i;while (1){i = ftell(fp1);printf("%d\t", i);ch = fgetc(fp1);i = ftell(fp1);printf("%d\t", i);if (feof(fp1))break;printf("%d\n", ch);}
}
int main() {//test_feof();//test_feof2();test_n();return 0;
}

输出:

0       1       97
1       3       10
3       5       10
5       6       98
6       8       10
8       10      10
10      10

因为在Windows中,从内存往磁盘存储时,换行符被当成两个字符处理,因此在读取的时候,使用fgetc读是只读到一个字符,但光标却是移动两格
在这里插入图片描述
这个不用深究,只需要知道在Windows系统读取磁盘中的存在这种现象就行。

相关内容

热门资讯

常用商务英语口语   商务英语是以适应职场生活的语言要求为目的,内容涉及到商务活动的方方面面。下面是小编收集的常用商务...
六年级上册英语第一单元练习题   一、根据要求写单词。  1.dry(反义词)__________________  2.writ...
复活节英文怎么说 复活节英文怎么说?复活节的英语翻译是什么?复活节:Easter;"Easter,anniversar...
2008年北京奥运会主题曲 2008年北京奥运会(第29届夏季奥林匹克运动会),2008年8月8日到2008年8月24日在中华人...
英语道歉信 英语道歉信15篇  在日常生活中,道歉信的使用频率越来越高,通过道歉信,我们可以更好地解释事情发生的...
六年级英语专题训练(连词成句... 六年级英语专题训练(连词成句30题)  1. have,playhouse,many,I,toy,i...
上班迟到情况说明英语   每个人都或多或少的迟到过那么几次,因为各种原因,可能生病,可能因为交通堵车,可能是因为天气冷,有...
小学英语教学论文 小学英语教学论文范文  引导语:英语教育一直都是每个家长所器重的,那么有关小学英语教学论文要怎么写呢...
英语口语学习必看的方法技巧 英语口语学习必看的方法技巧如何才能说流利的英语? 说外语时,我们主要应做到四件事:理解、回答、提问、...
四级英语作文选:Birth ... 四级英语作文范文选:Birth controlSince the Chinese Governmen...
金融专业英语面试自我介绍 金融专业英语面试自我介绍3篇  金融专业的学生面试时,面试官要求用英语做自我介绍该怎么说。下面是小编...
我的李老师走了四年级英语日记... 我的李老师走了四年级英语日记带翻译  我上了五个学期的小学却换了六任老师,李老师是带我们班最长的语文...
小学三年级英语日记带翻译捡玉... 小学三年级英语日记带翻译捡玉米  今天,我和妈妈去外婆家,外婆家有刚剥的`玉米棒上带有玉米籽,好大的...
七年级英语优秀教学设计 七年级英语优秀教学设计  作为一位兢兢业业的人民教师,常常要写一份优秀的教学设计,教学设计是把教学原...
我的英语老师作文 我的英语老师作文(通用21篇)  在日常生活或是工作学习中,大家都有写作文的经历,对作文很是熟悉吧,...
英语老师教学经验总结 英语老师教学经验总结(通用19篇)  总结是指社会团体、企业单位和个人对某一阶段的学习、工作或其完成...
初一英语暑假作业答案 初一英语暑假作业答案  英语练习一(基础训练)第一题1.D2.H3.E4.F5.I6.A7.J8.C...
大学生的英语演讲稿 大学生的英语演讲稿范文(精选10篇)  使用正确的写作思路书写演讲稿会更加事半功倍。在现实社会中,越...
VOA美国之音英语学习网址 VOA美国之音英语学习推荐网址 美国之音网站已经成为语言学习最重要的资源站点,在互联网上还有若干网站...
商务英语期末试卷 Part I Term Translation (20%)Section A: Translate ...