会错意表错情,搭错车上错床——“度日如年”的故事及“feof()”的故事

本文通过度日如年的故事引入,详细分析了C语言中feof()和fgetc()函数的常见误解及其正确使用方法,揭示了文件复制时常见的陷阱。

转自http://www.cnblogs.com/pmer/archive/2012/01/27/2330099.html

1. “度日如年”的故事

  一个幼儿园小盆友,看到了“度日如年”这个成语,以为是天天过年的意思,于是活学活用、借题发挥:“祝大家在新的一年里天天‘度日如年’”。
这就是会错了意,表错了情。
  “度日如年”的故事讲完了,下面是这个故事的C语言版。

2. feof()的故事

/*
例10.2 将一个磁盘文件中的信息复制到另一个磁盘文件中。今要求将上例建立的file1.dat文件中的内容复制到另一个磁盘文件file2.dat中。
*/
#include <stdio.h>
#include <stdlib.h>
int main()
{FILE *in,*out;
char ch,infile[10],outfile[10];
printf("输入读入文件的名字:");
scanf("%s",infile);
printf("输入输出文件的名字:");
scanf("%s",outfile); 
if((in=fopen(infile,"r"))==NULL)
   {printf("无法打开此文件\n");
   exit(0);   
   }
if((out=fopen(outfile,"w"))==NULL)
   {printf("无法打开此文件\n");
   exit(0);   
   }
while(!feof(in)) 
   {ch=fgetc(in);
    fputc(ch,out);
    putchar(ch); 
   } 
putchar(10);
fclose(in);
fclose(out);
return 0;
}

这段代码毛病很多,这里只谈其核心部分,即while语句部分。
  这条语句貌似首先“检查in所指的文件是否结束”,如果“!foef(in)”不为0,则从in流中读一个字符,然后写入out流。看起来很美,并且据说“运行结果是将file1.dat文件中的内容复制到file2.dat中。”
  然而,这只不过是一厢情愿的错觉而已。如果仔细检查一下就会发现,文件file2.dat比文件file1.dat长一个字节;如果手头有UltraEdit之类的十六进制编辑器,不难发现多出的字符的值很可能是FFH,即十进制的255。
  这个多出来的字符是怎么来的呢?主要原因有两个:对feof()函数的误解和对fgetc()函数的不求甚解:

为了知道对文件的访问是否完成,只须看文件读写位置是否移到文件的末尾。用feof函数可以检查到文件读写位置标记是否移到文件的末尾,即磁盘文件是否结束。feof(in)是检查in所指向的文件是否结束。如果是,则函数值为1(真),否则为0(假)。

    ————谭浩强 ,《C程序设计》(第四版),清华大学出版社,2010年6月,p340~341

fgetc:
调用形式:fgetc(fp)
功能:从fp指向的文件读入一个字符
返回值:读成功,带回所读的字符,失败则返回文件结束标志EOF(即-1)

    ————谭浩强 ,《C程序设计》(第四版),清华大学出版社,2010年6月,p338

  在这种错误认识的指导下,由于会错了意,难免会表错情。

3. feof()函数及fgetc()函数的真正含义

  首先,feof()函数并非“检查”“文件读写位置标记是否移到文件的末尾”,feof()函数检查的是流的end-of-file标记。end-of-file标记和读写位置标记虽然同属于FILE类型结构体记录的内容,但它们根本就是两回事。如果feof()函数检测到了end-of-file标记,返回一个int类型的非零值(不一定时1),否则返回int类型的0。
那么,end-of-file标记是记录流控制数据的FILE类型结构体对象中固有的吗?也不是,这个end-of-file标记是由fgetc()这样的函数所设置的。当fgetc()函数发现输入流中不存在数据后,除了返回一个EOF,还会设置FILE对象中的end-of-file标记,在很多实现中这个标记用一个“位”表示。
  由此可见,即使流中没有数据的情况下,feof()函数也不一定返回非零值。只有在流中没有数据并且fgetc()之类的函数继续读取失败之后,fgetc()函数才能检查到流的end-of-file标记。
  也就是说,feof()函数并不能告诉你流是否已经到了结尾,它所能告诉你的只不过是,而且仅仅是,前一次读取失败的原因是否因为到了流的结尾(读取失败的另一个原因是发生了错误)。
  为了说明这一点,下面进行一项测试。
  首先,在D:盘的根目录下建立一个文本文件ABC.TXT,并在其中写入ABC三个字符。
  然后,运行下面程序:

#include <stdio.h>
#include <stdlib.h>
int main( void )
{
   FILE *abc;
    
   if((abc = fopen("D:\\ABC.TXT","rb")) == NULL )
   {
      printf("打开文件失败\n");
      return !0;
   }
    
   for(int i = 0 ; i < 5 ; i++ )
   {
      int ch;
      int eof_before_read,eof_after_read;
      eof_before_read = feof(abc) ;
      ch = fgetc( abc );
      eof_after_read = feof(abc) ;
      printf(
             "读入%c(%d)前后feof()的值分别为:%d,%d\n",
             ch,ch,eof_before_read,eof_after_read
            );
   } 
   fclose(abc);
   return 0;
}

这段程序的运行结果是:
  读入A(65)前后feof()的值分别为:0,0
  读入B(66)前后feof()的值分别为:0,0
  读入C(67)前后feof()的值分别为:0,0
  读入(-1)前后feof()的值分别为:0,16
  读入(-1)前后feof()的值分别为:16,16
  由此不难看出,在读入C之后(已经到了流的结尾),feof()函数的返回值依然是0,只是再次试图读取字符之后,feof()的返回值才成了16。这是由于fgetc()函数发现已经没有字符可读,在对应的FILE结构体数据中设置了end-of-file标记的缘故。

4. feof()函数的真正用途

  feof()函数只能事后诸葛亮地告诉我们读入是如何结束的,它根本不能用于拷贝的循环控制。把feof()函数用于拷贝的循环控制,不但是会错意表错情,而且简直是搭错了车上错了床。
  feof()函数的正确用法之一是:

#include <stdio.h>
#include <stdlib.h>
 
void file_copy(FILE * , FILE * );
 
int main( void )
{
   FILE *abc;
   FILE *abc_b;
    
   if((abc = fopen("D:\\ABC.TXT","rb")) == NULL )
   {
      printf("打开文件失败\n");
      return EXIT_FAILURE;
   }
    
   if((abc_b = fopen("D:\\ABC_B.TXT","wb")) == NULL )
   {
      printf("打开文件失败\n");
      fclose(abc);
      return EXIT_FAILURE;
   }
 
   file_copy( abc_b ,  abc  );
    
   if( feof(abc) != 0 )
   {
      printf("拷贝正常结束\n");
      fclose(abc);
      fclose(abc_b);
      return EXIT_SUCCESS;
   }     
       
   if( ferror (abc) != 0 )
   {
      printf("拷贝过程中发生错误,目标文件可能并不正确\n");
      fclose(abc);
      fclose(abc_b);
      return EXIT_FAILURE;
   }      
 
}
 
void file_copy( FILE * t, FILE *s )
{  
   int ch;
   while( (ch = fgetc(s) ) != EOF )
      fputc( ch , t );
 
}


内容概要:本文系统介绍了算术优化算法(AOA)的基本原理、核心思想及Python实现方法,并通过图像分割的实际案例展示了其应用价值。AOA是一种基于种群的元启发式算法,其核心思想来源于四则运算,利用乘除运算进行全局勘探,加减运算进行局部开发,通过数学优化器加速函数(MOA)和数学优化概率(MOP)动态控制搜索过程,在全局探索与局部开发之间实现平衡。文章详细解析了算法的初始化、勘探与开发阶段的更新策略,并提供了完整的Python代码实现,结合Rastrigin函数进行测试验证。进一步地,以Flask框架搭建前后端分离系统,将AOA应用于图像分割任务,展示了其在实际工程中的可行性与高效性。最后,通过收敛速度、寻优精度等指标评估算法性能,并提出自适应参数调整、模型优化和并行计算等改进策略。; 适合人群:具备一定Python编程基础和优化算法基础知识的高校学生、科研人员及工程技术人员,尤其适合从事人工智能、图像处理、智能优化等领域的从业者;; 使用场景及目标:①理解元启发式算法的设计思想与实现机制;②掌握AOA在函数优化、图像分割等实际问题中的建模与求解方法;③学习如何将优化算法集成到Web系统中实现工程化应用;④为算法性能评估与改进提供实践参考; 阅读建议:建议读者结合代码逐行调试,深入理解算法流程中MOA与MOP的作用机制,尝试在不同测试函数上运行算法以观察性能差异,并可进一步扩展图像分割模块,引入更复杂的预处理或后处理技术以提升分割效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值