zoukankan      html  css  js  c++  java
  • 为什么用feof()或者iostream::eof来控制循环是不好的

    例1:

    data.txt为:

    data.txt十六进制如下:

    可看出结尾有一个换行符(注:Windows下换行符为 )。

    #include <stdio.h>
    
    int main()
    {
    	FILE *fp;
    	int i = 0;
    	char s[3][7];
    
    	if ((fp = fopen("data.txt", "r")) == NULL)
    		printf("Error: can't open file
    ");
    
    	else {
    		while (!feof(fp)) {
    			fgets(s[i], 7, fp);  // 读取一行 循环第一次读入后s[0][0]='1',s[0][1]=' ',...s[0][5]='
    ',s[0][6]=''
    			printf("%s", s[i]); // 输出 
    			i++;
    		}
    		printf("
    ");
    		printf("i = %d
    ", i);
    
    		fclose(fp);
    	}
    
    	return 0;
    }

    运行结果为:

    结果分析:

    注意:fgets函数遇到换行符时,会将换行符读入,并紧跟换行符后加入一个''

    在stdio.h中feof的定义如下:

    #define _IOEOF 0x0010
    #define feof(_stream) ((_stream)->_flag & _IOEOF)

    由此可知,只有当fp->_flag & 0x0010为真时,才认为文件结束了。只有当file position indicator(在Windows上是fp->_ptr)到了文件末尾,然后再发生读/写操作时,fp->_flag才会被置为含有 _IOEOF,然后再调用feof(),才会得到文件结束的信息。并不是file position indicator一指到文件尾,feof()就认为文件结束。所以该例中会第4次进入循环体,而第4次执行fgets(s[i], 7, fp);时,fp已指向文件尾,所以并没有任何数据读入s[3],s[3]为乱码(下标已越界)。

    循环条件可以用feof,但必须记住:当文件刚刚读到文件尾时,feof不会返回true。只有在文件尾部再次进行一次读操作,feof才会返回真。

    可对例1做如下几种修改:

    修改1:循环体内加入一行 if (!feof(fp))判断

    #include <stdio.h>
    
    int main()
    {
    	FILE *fp;
    	int i = 0;
    	char s[3][7];
    
    	if ((fp = fopen("data.txt", "r")) == NULL)
    		printf("Error: can't open file
    ");
    
    	else {
    		while (!feof(fp)) {
    			fgets(s[i], 7, fp);  // 读取一行 循环第一次读入后s[0][0]='1',s[0][1]=' ',...s[0][5]='
    ',s[0][6]=''
    
    			if (!feof(fp)) // 加入此行
    				printf("%s", s[i]); // 输出 
    			i++;
    		}
    		printf("
    ");
    		printf("i = %d
    ", i);
    
    		fclose(fp);
    	}
    
    	return 0;
    }

    输出:

    修改2:将data.txt文件末尾的换行符去掉

    即data.txt 为:

    十六进制如下:

    运行例1中代码,输出如下:

    说明结尾没有换行符,则第3次循环即遇到了文件尾。

    注:Linux中的Vim结尾会自动加一个换行符 ,

    去除vim文件尾的默认换行符:

    1.vim -b filename

    2.命令行模式:set noeol  (注:noeol = no end-of-line)

    修改3:将fgets做为循环条件

    #include <stdio.h>
    
    int main()
    {
    	FILE *fp;
    	int i = 0;
    	char s[3][7];
    
    	if ((fp = fopen("data.txt", "r")) == NULL)
    		printf("Error: can't open file
    ");
    
    	else {
    		while (fgets(s[i], 7, fp) != NULL) {		
    			printf("%s", s[i]); // 输出 
    			i++;
    		}
    		printf("
    ");
    		printf("i = %d
    ", i);
    
    		fclose(fp);
    	}
    
    	return 0;
    }

    输出:

    建议使用修改3。

    例2:

    1.txt为:

    例2中是逐个读取字符,因此1.txt结尾有无换行符结果都一样。

    #include <iostream>
    #include <fstream>
    #include <cassert>
    #include <string>
    using namespace std;
    
    // 逐个字符读入(忽略空格与回车)
    void readtxt(string file)
    {
    	ifstream infile;
    	infile.open(file.data());   // 将文件流对象与文件连接起来
    	assert(infile.is_open());   // 若失败,则输出错误消息,并终止程序运行
    
    	while (!infile.eof())
    	{
    		char c;
    		infile >> c;  // 类似cin>>。 c = infile.get();和 infile.get(c);效果一模一样!
    					
    		cout << c << endl;
    	}
    	infile.close();             // 关闭文件输入流
    }
    
    int main()
    {
    	readtxt("1.txt");
    	return 0;
    }
    
    

    输出:

    将char c;修改为char c = 65;

    #include <iostream>
    #include <fstream>
    #include <cassert>
    #include <string>
    using namespace std;
    
    // 逐个字符读入(忽略空格与回车)
    void readtxt(string file)
    {
    	ifstream infile;
    	infile.open(file.data());   // 将文件流对象与文件连接起来
    	assert(infile.is_open());   // 若失败,则输出错误消息,并终止程序运行
    
    	while (!infile.eof())
    	{
    		char c = 65;  // 将char c;修改为char c = 65;
    		infile >> c;  // 类似cin>>。 c = infile.get();和 infile.get(c);效果一模一样!
    					
    		cout << c << endl;
    	}
    	infile.close();             // 关闭文件输入流
    }
    
    int main()
    {
    	readtxt("1.txt");
    	return 0;
    }
    

    输出:

    可看出第5次循环执行 infile >> c; 后,对c无任何影响,c维持上次状态。

    对例2进行如下修改:

    #include <iostream>
    #include <fstream>
    #include <cassert>
    #include <string>
    using namespace std;
    
    // 逐个字符读入(忽略空格与回车)
    void readtxt(string file)
    {
    	ifstream infile;
    	infile.open(file.data());   // 将文件流对象与文件连接起来
    	assert(infile.is_open());   // 若失败,则输出错误消息,并终止程序运行
    
    	while (!infile.eof())
    	{
    		char c = 65;
    		infile >> c;  // 类似cin>>。 c = infile.get();和 infile.get(c);效果一模一样!该读取要在infile.fail()函数的前面。 
    
    	    if (infile.fail()) {  // 如果是文件尾,则if (infile.eof()) { 也行。if (infile.fail()) break;也行
    					  
    		cout << "
    文件读取错误或到达文件尾" << endl;
    					  
    		exit(1);
    					  
    		} 
    					
    		cout << c << endl;
    	}
    	infile.close();             // 关闭文件输入流
    }
    
    int main()
    {
    	readtxt("1.txt");
    	return 0;
    }

    输出:

    注: 在几乎所有情况下,若设置 eofbit ,则一同设置 failbit 。 到达文件尾,infile.eof()和infile.fail()都为真。

    补充:

    人们经常误认为 EOF 是从文件中读取的一个字符。其实,EOF 不是一个字符,它被定义为是 int 类型的一个负数(-1)。EOF 也不是文件中实际存在的内容。EOF 也不是只表示读文件到了结尾这一状态(这种状态可以用 feof() 来检测),它还能表示 I/O 操作中的读、写错误(通常可以用 ferror() 来检测)以及其它一些关联操作的错误状态。

    很多人说循环条件EOF只能用于处理文本文件,其实不然,这点不是特别的准确,还要看定义的变量的类型。

    下面这段程序对文本文件和二进制文件都可以:

    int c;
    while((c=fgetc(fp)) != EOF)
    {
        printf("%X/n", c); 
    }


    如果读到了FF,由于c定义为int型,所以实际上c=0x000000FF,不等于EOF(-1=0xFFFFFFFF),因此不会误判为文件结尾。

    但是如果把c定义为char类型,就有可能产生混淆了。

    char c;
    while((c=fgetc(fp)) != EOF)
    {
        printf("%X/n", c); 
    }


    因为文本文件中存储的是ASCII码,而ASCII码中FF一般不使用,所以如果读文件返回了FF,说明已经到了文本文件的结尾。但是如果是二进制文件,其中可能会包含FF,因此不能把读到EOF作为文件结束的条件,此时只能用feof()函数。

    循环条件!feof(fp)可用于处理文本文件和二进制文件,如果运行如下程序:

    char c;
    while(!feof(fp))  // while(c != EOF)同理
    {
        c = fgetc(fp);
        printf("%d/n", c); 
    }


    会发现最后输出了一个-1,原因就是在读完最后一个字符后,fp->flag仍然没有被置为_IOEOF,因而feof()仍然没有探测到文件结尾。直到再次调用fgetc()执行读操作,feof()才能探测到文件结尾。这样就多输出了一个-1(即EOF)。

    可修改如下:

    char c;
    c = fgetc(fp);
    while(!feof(fp))  // 或 while(c != EOF)
    {
        printf("%d/n", c); 
        c = fgetc(fp);
    } 
  • 相关阅读:
    css(一)
    Html table
    Html
    jQuery事件和效果
    jQuery基础
    ajax和http状态码
    点击数组选中某一项,存入数组,再次点击删除
    单行两行,多余显示省略号
    git的使用
    产生滚动效果
  • 原文地址:https://www.cnblogs.com/a3192048/p/12241241.html
Copyright © 2011-2022 走看看