【被误用的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来打印错误信息。

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

  • 13
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 11
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

维生素C++

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值