被误用的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;
}

三、后记

 相信这篇博客可以让你意识到feof的正确用法,避免产生意向不到的错误。本篇博客部分内容参考学习自以下的文献,大家有兴趣可以阅读加深理解。
C语言教材对feof函数的常见误解和误用

  • 41
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 14
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

罅隙`

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

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

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

打赏作者

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

抵扣说明:

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

余额充值