当前位置: 首页 > news >正文

【被误用的feof与文件操作读取结束的正确判定】

学习导航

一、被误用的feof

从函数的名字来看,大家很容易把函数的功能理解成:检测是是否达到文件的结尾。但事实真的如此吗?我们首先来查阅feof函数的返回值:

所以可以确定的是,如果feof检测出文件结束志标记则返回一个非零值。但这里需要注意,feof检测的是 “文件结束标记” ,而不是 “文件位置标记” ,这在FILE结构体中属于两种不同的标记。文件到达结尾只是说明文件的位置标记达到末尾,不能说明文件结束标记的情况,而只有文件结束标记才能使feof的返回值为真。
 我们可以用下面的代码来说明上面的结论,预先使test.txt的内容为空。

int main()
{
	FILE* fp = fopen("test.txt", "r");
	if (fp == NULL)
	{
		printf("%s", strerror(errno));
		return 0;
	}
	for (int i = 0; i < 2; i++)
	{
		printf("feof() = %d\n", feof(fp));
		fgetc(fp);
	}
	fclose(fp);
	fp = NULL;
	return 0;
}

打印结果如下:

feof() = 0;
feof() = 1;
可以看到即使到达了文件结尾,feof也不能立刻检测出。那为什么第二次就可以呢?其实原因在于fgetc发挥了作用。根据C标准对该函数的说明,当fgetc返回值为EOF时且确实已经到达文件结尾时(返回值为EOF可能有其他的情况),fgetc函数会自动设置文件结束标记。

所以换言之,feof函数并不能检测是否达到文件结尾,它只能测出文件读取失败是否由于文件读取结束引起的。文件读取失败对于fgetc函数来说表现在返回值为EOF。

二、正确的判断方法

正确的做法是根据函数的返回值来进行判断,现在我们对常用函数进行逐一说明:

①fgetc函数

fgetc函数需要检测返回值是否为EOF
如果到达文件结尾,文件结束标志被fgetc函数设置,可以通过feof函数检测出
如果发生读取错误,文件错误标志被fgetc函数设置,可以通过ferror函数检测出

int main()
{
	FILE* fp = fopen("test.txt", "r");
	if (fp == NULL)
	{
		printf("%s", strerror(errno));
		return 0;
	}
	int c = 0;
	while ((c = fgetc(fp)) != EOF)
	{
		putchar(c);
	}
	//判断是什么原因结束的
	if (ferror(fp))
	{
		printf("I/O error when reading\n");
	}
	else if (feof(fp))
	{
		printf("End of file reached successfully\n");
	}
	flose(fp);
	fp = NULL;
	return 0;
}
②fgets

fgets函数需要检测返回值是否为NULL
如果到达文件结尾,文件结束标志被fgets函数设置,可以通过feof函数检测出
如果发生读取错误,文件错误标志被fgets函数设置,可以通过ferror函数检测出

③fread函数

对于fread函数我们需要检测实际返回的读取个数和预期读取个数
如果实际读取个数和预期读取个数不同,说明要么发生读取错误,要么达到文件结尾,那么相应的文件错误标志和文件结束标志也被设置。可以通过ferror和feof函数进行检验。

#define SIZE 5
int main(void)
{
	double src[SIZE] = { 1.0, 2.0, 3.0, 4.0, 5.0 };
	double buf[SIZE] = {0};
	FILE* fp = fopen("test.bin", "wb"); // 必须用二进制模式
	fwrite(src, sizeof(src[0]), SIZE, fp); // 写 double 的数组
	fclose(fp);

	fp = fopen("test.bin", "rb");
	size_t ret_code = fread(buf, sizeof(buf[0]), SIZE, fp); // 读 double 的数组
	if (ret_code == SIZE) 
	{
		puts("Array read successfully, contents: ");
		for (int n = 0; n < SIZE; ++n) 
			printf("%f ", buf[n]);
		putchar('\n');
	}
	else // error handling
	{ 
		if (feof(fp))
			printf("Error reading test.bin: unexpected end of file\n");
		else if (ferror(fp)) 
			perror("Error reading test.bin");
	}
	fclose(fp);
	fp = NULL;
	return 0;
}

三.总结

**fgetc、fgets、fread才是判断文件是否读取结束的,

  1. 如果fgetc返回EOF则文件读取结束,否则没结束返回当前文件位置标记的ASCII码值。
  2. 如果fgets返回NULL则文件读取结束,否则返回当前文件位置标记的地址
  3. 如果fread返回的实际值即读的实际个数小于预期读的个数说明读取结束,否则没有读取结束**

但是上面三种方式都只是判断文件是否读取结束,但是并不知道是正常读取结束还是异常导致的读取结束,所以我们要通过feof来判断,如果返回非0值,是正常读取结束,否则是异常读取结束,用ferror来打印错误信息。

小编制作不易,点个小猪猪,谢谢大家!

相关文章:

  • Adobe Premiere Pro:掌控视频剪辑的魔法之手,让你的创作腾飞!
  • 【算法|动态规划No.17】leetcode64. 最小路径和
  • jar 命令启动java 指定配置文件路径 jar如何启动
  • 「才得吹嘘身渐稳」,也来谈谈大模型
  • 95740-26-4|用于体内DNA合成的探针F-ara-EdU
  • MATLAB算法实战应用案例精讲-【图像处理】SLAM技术详解
  • CTF Misc(3)流量分析基础以及原理
  • Springboot 集成 Redis集群配置公网IP连接报私网IP连接失败问题
  • 数据挖掘与统计分析——T检验,正态性检验和一致性检验——代码复现
  • idea 插件推荐(持续更新)
  • 20231009-学习笔记
  • 【新书推荐】当 Python 遇到 ChatGPT —— 自动化办公落地
  • AI如何帮助Salesforce从业者找工作?
  • Qt如何实现动态背景-视频背景
  • Hazelcast系列(八):数据结构
  • Vue整合
  • 在linux下的vim中使用内联函数时,会有未定义的引用错误解决办法
  • 如何获取standard cell各端口的作用
  • flutter StreamSubscription 订阅者 stream
  • JAVA-SpringBoot入门Demo用IDEA建立helloworld
  • 江西暴雨强对流明显,专家:落雨区高度重叠,地质灾害风险高
  • 长江画派创始人之一、美术家鲁慕迅逝世,享年98岁
  • 国家主席习近平在莫斯科出席红场阅兵式
  • 98年服装“厂二代”:关税压力下,我仍相信中国供应链|湃客Talk
  • 高盛上调A股未来12个月目标点位,沪深300指数潜在回报15%
  • 金融监管总局:近五年民企贷款投放年平均增速比各项贷款平均增速高出1.1个百分点