feof如何跳出循环问题

  • 介绍一下IO操作的基础知识

(1) long ftell(FILE *stream);

功能:用于得到文件位置指针当前位置相对于文件首的偏移字节数,即可计算得到stream的当前位置

(2)int fseek(FILE *stream, long offset, int origin)

功 能:函数设置文件指针stream的位置。如果执行成功,stream将指向以stream的位置为基准,偏移offset(指针偏移量)个字节的位置,函数返回0。

如果执行失败(比如offset超过文件自身大小),则不改变stream指向的位置,函数返回一个非0值。

第三个参数:

SEEK_SET: 文件开头

SEEK_CUR: 当前位置

SEEK_END: 文件结尾

(3)int feof(FILE *stream)

功能:检测流上的文件结束符,判断文件是否结束,返回值为0时表示还没有结束,否则结束!

其他的打开读取写入等就不介绍了。

  • 介绍下使用feof出现的两种错误

以txt中存储的是A-Z的字符为例

(1)feof和fseek搭配使用导致跳不出循环

在实际使用中我们会使用fseek调到某字节处,比如下面代码

int main() 
{
	FILE *fp;
	fopen_s(&fp, "log.txt", "r");
	if (NULL == fp) 
	{
		perror("fopen fail");
		return 1;
	}
	char flag;
	int pos = 0;

	while (!feof(fp))
	{
		pos = ftell(fp);
		fread(&flag, 1, 1, fp);
		printf("after pos: %d, %c\n", pos, flag);
		fseek(fp, 5, SEEK_CUR);
	}

	fclose(fp);
	system("pause");
	return 0;
}

 得到的结果是无限循环的

原因分析:

使用了fseek后就无法跳出循环了

找到C语言关于fseek的文档:

Sets the position indicator associated with the stream to a new position defined by adding offset to a reference position specified by origin. The End-of-File internal indicator of the stream is cleared after a call to this function, and all effects from previous calls to ungetc are dropped. When using fseek on text files with offset values other than zero or values retrieved with ftell, bear in mind that on some platforms some format transformations occur with text files which can lead to unexpected repositioning. On streams open for update (read+write), a call to fseek allows to switch between reading and writing.

译为:

将与流关联的位置指示器设置为通过向原点指定的引用位置添加偏移量而定义的新位置。在调用此函数后,将清除流的文件结束内部指示器,并删除以前调用ungetc时产生的所有效果。在文本文件上使用非零偏移值的fseek或使用ftell检索的值时,请记住,在某些平台上,文本文件会发生一些格式转换,这可能导致意外的重新定位。在打开更新流(读+写)时,对fseek的调用允许在读和写之间进行切换。

其大体意思就是使用了fseek后就会消除到文件的结束标志位,导致feof不起作用。

所以使用feof时候不能使用fseek,如果想要调转到某一个字节可以使用fread,fread在读取的时候也有移动指针位置的作用。(注掉上面代码中的fseek那一行就可以看到pos是变化的)
 

int main() 
{
	FILE *fp;
	fopen_s(&fp, "log.txt", "r");
	if (NULL == fp) 
	{
		perror("fopen fail");
		return 1;
	}
	char flag;
	int pos = 0;

	while (!feof(fp))
	{
		pos = ftell(fp);
		fread(&flag, 1, 1, fp);
		printf("after pos: %d, %c\n", pos, flag);

		//法一:移动的是变量的话可以使用for循环来移动到某位
		//for (int i = 0; i < 5; i++)
		//	fread(&flag, 1, 1, fp);
        //法二:移动的是常量的话可以移动到一个数组中
		char c[5];
		fread(c, 5, 1, fp);
		//fseek(fp, 5, SEEK_CUR); //移动error
	}

	fclose(fp);
	system("pause");
	return 0;
}

(2)feof循环时多输出一次

int main()
{
	FILE *fp;
	fopen_s(&fp, "log.txt", "r");
	if (NULL == fp) 
	{
		perror("fopen fail");
		return 1;
	}
	char flag;
	int pos = 0;

	while (!feof(fp))
	{
		pos = ftell(fp);
		fread(&flag, 1, 1, fp);
		printf("after pos: %d, %c\n", pos, flag);
	}

	fclose(fp);
    return 0;
}

原因分析:

在读完文件的最后一个字符后,fp->flag没有被置为_IOEOF,因而feof()没有探测到文件结尾。直到再次调用fgetc()或者fread执行读操作,feof()才能探测到文件结尾。

解决方法:

1、在进入while循环前先读取一次(不推荐

int main() 
{
	FILE *fp;
	fopen_s(&fp, "log.txt", "r");
	if (NULL == fp) 
	{
		perror("fopen fail");
		return 1;
	}
	char flag;
	int pos = 0;
	fread(&flag, 1, 1, fp);
	while (!feof(fp))
	{
		pos = ftell(fp);
		fread(&flag, 1, 1, fp);
		printf("after pos: %d, %c\n", pos, flag);
	}

	fclose(fp);
	system("pause");
	return 0;
}

这样可以跳出循环,但是第一次的数值就浪费了,并且最后一次也不对,多读了一次Z。

2.通过长度来跳出while循环(推荐

int main() 
{
	FILE *fp;
	fopen_s(&fp, "log.txt", "r");
	if (NULL == fp) 
	{
		perror("fopen fail");
		return 1;
	}
	char flag;
	int pos = 0;

	//计算文件大小
	fseek(fp, 0, SEEK_END);
	long size = ftell(fp);
	fseek(fp, 0, SEEK_SET);

	while (size != ftell(fp))
	{
		pos = ftell(fp);					
		fread(&flag, 1, 1, fp);
		printf("after pos: %d, %c\n", pos, flag);
	}

	fclose(fp);
	system("pause");
	return 0;
}


该方法是通过fseek调转到文件尾先计算出文件的字节数size,然后再跳转到文件头开始读取,知道fp指向的位置满足size大小,则退出

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值