更新时间:2015-06-27 01:01:29浏览次数:1+次
本文是朋友提出的一个问题,即读取一个文件并写入一个新文件,新文件结尾多出了一个字符。而代码看上去很正常,结果出人意料。这里通过逐步的分析,层层递进,一步一步的把问题的原因剖析出来,并提出了两种解决办法。你可以从此分析过程中,领略到跟踪问题的方法和思路,同时也可以对此问题有更加深入的理解。给出的解决办法,也可以切实的解决实际问题。
一朋友问我了一个问题,说以下代码将一个文件的内容读取出来,然后逐个字符的写入到另一个文件,然后完成后,新文件竟然多了一个文件。很疑问,代码看上去似乎都是正常的。
代码如下:
#include <stdio.h>
void main()
{
FILE * fp=0;
FILE * fp2=0;
fp = fopen("1.txt","r");
fp2= fopen("2.txt","w");
char ch;
while (!feof(fp))
{
ch = fgetc(fp);
fputc(ch,fp2);
}
fclose(fp);
fclose(fp2);
}
问题水落石出,如何解决呢?我们知道了这是循环判断出问题了,但是除了这个写法,大家都不知道如何去判断。所以,我开始就在内部添加一个队ch变量的字符检测,只有不等于EOF,才会写入新文件,否则忽略。这样,循环在下一次就结束了,不会再进入循环了。如此就解决了这个问题。
在循环内部添加检测的代码如下:
#include <stdio.h>
void main()
{
FILE * fp=0;
FILE * fp2=0;
fp = fopen("1.txt","r");
fp2= fopen("2.txt","w");
char ch;
while (!feof(fp))
{
ch = fgetc(fp);
if (ch!=EOF)
{
fputc(ch,fp2);
}
}
fclose(fp);
fclose(fp2);
}
但是本着认真研究问题的态度,所以,继续了探索。试图找到为什么判断会错误的原因。不想在内部检测。
所以,就跟踪了循环的6次,对fp进行跟踪分析。然后将fp结构的内部参数的变化做了记录。FILE结构的说明,参见文章《C语言的文件操作深入分析之FILE结构体和文件操作机制》。
通过分析参数变化发现,FILE的_cnt初始化时是0,读取第一个字符时,变成总字符数,读完一个字符,此数字减一。读完最后一个字符变成0。其他各种参数没有变化。然后继续读取最后一个EOF,此时_cnt依然为零。此时的FILE的_ptr是指示当前指针的值,表示当前读取到哪里。读取了EOF后,_ptr就变成了_base一样的值。_base是文件起始的地址。表示当前文件指针回到了起始地址。_flag变成由9变成了25。说明文件读取状态已经发生了变化。此时的状态涵盖了文件读取到了文件尾的信息。此时读取文件就结束。使用feof就可以检测到文件读取完毕了。
通过跟踪feof函数发现,函数内部实际就是检测_flag参数,将其与_IOEOF(0x0010)位与来检查是否读取完毕的。当读完EOF后,feof就检测到了文件尾,所以就不再进入循环。而读完字符6之后,指针只是指向了文件尾,但是这些状态还没有变过来,所以,feof检测就失败。因此就进入了循环。
那么,既然如此,我们就不能依赖feof来做检测,或者一定要在循环内部做进一步的检测。如果不想在循环内部做检测,那么,就在循环条件判断换一种,不要使用feof了。使用的新方法,便是对_cnt来检测,即读取剩余的字符数来检测。因为一开始,它是0,所以,我们不能使用while,应该使用do while来做。这时候,没有比do while更适合的写法了。读取了一个字符之后,_cnt的值就是剩下的字符数了,之后就用后面的while正常检测,一旦变成零,即退出循环。
在循环外检测文件读取状态的代码如下:
#include <stdio.h>
void main()
{
FILE * fp=0;
FILE * fp2=0;
fp = fopen("1.txt","r");
fp2= fopen("2.txt","w");
char ch;
do
{
ch = fgetc(fp);
fputc(ch,fp2);
}while (((fp)->_cnt != 0));
fclose(fp);
fclose(fp2);
}
这样也就完美解决了这个问题。但是一般情况下,你不清楚feof内部的工作机制,也不知道文件读取的机制,也不知道如何自己检测,那么就在循环内部简单的加个判断即可。如果很清楚,使用后面的代码,一样解决了。
相关资讯