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系统读取磁盘中的存在这种现象就行。

相关内容

热门资讯

笑傲江湖周云鹏的脱口秀台词 笑傲江湖周云鹏的脱口秀台词  地球人都是中国有个喜剧之王周星驰,而周云鹏才是脱口秀中的剧场之王!看过...
yy活动主持词 yy活动主持词  女:亲爱的朋友们!大家晚会好!我是今晚的主持人XXX,欢迎您走进“鹊桥会”——丫丫...
升国旗仪式主持词 升国旗仪式主持词(精选12篇)  主持词可以采用和历史文化有关的表述方法去写作以提升活动的文化内涵。...
主持词范文 主持词范文  主持词写作  主持词的写作没有固定格式,它的最大特点就是富有个性。不同内容的活动,不同...
主持人正式节目优秀串词 主持人正式节目优秀串词  第一篇:《6.1主持人正式节目串词》  尊敬的老师们、亲爱的同学们大家好:...
篮球赛闭幕式主持词 篮球赛闭幕式主持词范文3篇  篇一:篮球赛闭幕式主持词尊敬的各位领导、各位来宾,裁判员、教练员、运动...
公司晚会主持稿 公司晚会主持稿【三篇】  在现实社会中,各种主持稿频频出现,主持稿大体上可分为会议主持稿、文艺演出晚...
红歌会主持词 红歌会主持词范文  相信我们身边会有喜欢唱红歌的人,下面小编为大家带来了2篇红歌会主持词范文,欢迎大...
圣诞晚会主持词开场白 圣诞晚会主持词开场白(通用12篇)  在社会发展不断提速的今天,我们使用到开场白的机会越来越多,独具...
新春年会主持词 新春年会主持词范文(精选5篇)  主持词的写作要突出活动的主旨并贯穿始终。在当下的社会中,各种集会中...
老年人生日司仪主持词 老年人生日司仪主持词  主持词要把握好吸引观众、导入主题、创设情境等环节以吸引观众。我们眼下的社会,...
庆元旦主持词 精选庆元旦主持词3篇  主持人在台上表演的灵魂就表现在主持词中。在当下的中国社会,各种场合中活跃现场...
农村结婚典礼司仪主持词 农村结婚典礼司仪主持词(通用6篇)  主持词是主持人在台上表演的灵魂之所在。在人们越来越多的参与各种...
升学宴学生家长致辞 升学宴学生家长致辞  在日复一日的学习、工作或生活中,要用到致辞的地方还是很多的,致辞具有能伸能缩,...
家长道德讲堂主持词 家长道德讲堂主持词  道德讲堂就是一个引导人们讲道德,让人长好心的地方。崇德向善是我们中华民族的传统...
沙龙活动主持词 沙龙活动主持词(通用9篇)  主持词是各种演出活动和集会中主持人串联节目的串联词。在人们越来越多的参...
《纨绔》的经典台词 《纨绔》的经典台词  1、庸脂俗粉算得了什么?狐王才是真绝色。傻一时且说天作孽。傻一世便是自作孽了。...
电影王家卫堕落天使经典台词 电影王家卫堕落天使经典台词  李嘉欣和黎明相对无言。  李嘉欣扬着脸,拿烟的手略微颤抖,一枚偌大的闪...
升旗仪式主持词 升旗仪式主持词  主持词是各种演出活动和集会中主持人串联节目的串联词。在当今不断发展的世界,主持词是...
欢乐颂的经典台词有哪些 欢乐颂的经典台词有哪些  《欢乐颂》讲述了同住在欢乐颂小区22楼的五个来自不同家庭、性格迥异的女孩们...