C语言文件补充笔记1:EOF与feof
创始人
2024-05-12 03:33:48
0

1 关于EOF

可以查看EOF的宏定义
在这里插入图片描述
函数fgetc如果读取失败就返回-1,对于文本文件而言,以为着读取结束,因此-1可以作为结束的标志。

#define _CRT_SECURE_NO_WARNINGS
#include
int main() {FILE* fp = fopen("a.txt", "r");if (NULL == fp){perror("open");	return -1;		//在main函数中,按照程序开发的一般惯例,成功则返回0,失败则返回-1}char ch;while ((ch = fgetc(fp))!=-1){printf("%c", ch);}printf("\n");return 0;
}

刚刚查看宏定义可知EOF就是-1,因此上面的循环判断可以用EOF替代
这里需要注意的一点是,可以用fgetc的返回值是否为-1来判断文件是否读取结束,但并不意味着文件最后一个字符为-1

#define _CRT_SECURE_NO_WARNINGS
#include
int main() {FILE* fp = fopen("a.txt", "r");if (NULL == fp){perror("open");	return -1;		//在main函数中,按照程序开发的一般惯例,成功则返回0,失败则返回-1}char ch;while ((ch = fgetc(fp))!=EOF)	//这里原来是-1,现在改成了EOF{printf("%c", ch);}printf("\n");return 0;
}

2 文件中包含二进制数字

假如-1(EOF对应数字)被写进了文件中,那么读取的时候,也会被正常读出来,如果仍然用fputc的返回值是否为-1来判断文件是否读取结束,那么就会出现bug。
例如:

#define _CRT_SECURE_NO_WARNINGS
#include
int main() {FILE* fp = fopen("a.txt", "w");if (NULL == fp){perror("open");	return -1;		//在main函数中,按照程序开发的一般惯例,成功则返回0,失败则返回-1}char a[10] = { 97, 98, 99, -1, -2, 100 };int i = 0;while (a[i]!=0){fputc(a[i], fp);i++;}return 0;
}

上面的程序是把字符数组写入文件,字符数组a中,有一个元素是-1,写入之后,打开a.txt文件,内容如下:
在这里插入图片描述
-1和-2无法根据ASCII码确定字符,因此乱码
我们再以EOF作为文件结束标志,读取文件

#define _CRT_SECURE_NO_WARNINGS
#include
int main() {FILE* fp = fopen("a.txt", "r");char ch;while ((ch = fgetc(fp)) != EOF) printf("%c", ch);return 0;
}

输出

abc

也就是说,当读到-1的时候,就退出了while循环,后面的不会正常打印

3 feof函数

为了避免上述问题的产生,可以使用 feof函数

#define _CRT_SECURE_NO_WARNINGS
#include
int main() {FILE* fp = fopen("a.txt", "r");char ch;while (!feof(fp)){ch = fgetc(fp);printf("%c", ch);	//feof(fp)返回0表示没有到文件末尾,继续读}printf("\n");return 0;
}

在这里插入图片描述

5 一个程序中多个文件指针打开同一个文件

上面的程序,假如写和读在同一个源程序中,但写指针和都指针使用不同的名字,程序如下:

#define _CRT_SECURE_NO_WARNINGS
#include
int main() {FILE* fp1 = fopen("a.txt", "w");if (NULL == fp1){perror("open");	return -1;		//在main函数中,按照程序开发的一般惯例,成功则返回0,失败则返回-1}char a[10] = { 97, 98, 99, -1, -2, 100 };int i = 0;while (a[i]!=0){fputc(a[i], fp1);i++;}FILE* fp2 = fopen("a.txt", "r");char ch;while ((ch = fgetc(fp2)) != EOF) printf("%d", ch);printf("  %d\n", ch);return 0;
}

输出

  -1

查看a.txt文件,可以发现成功将97, 98, 99, -1, -2, 100对应的字符写入,但后面却无法通过fp2读取,并且ch取到的只有-1,即读取失败。具体原因我不知道,但一个程序中,不能出现多个文件指针指向同一个文件

5 文件复制

#define _CRT_SECURE_NO_WARNINGS
#includeint main() {FILE* fp1 = fopen("a.txt", "r");if (NULL == fp1){perror("open");return -1;		//在main函数中,按照程序开发的一般惯例,成功则返回0,失败则返回-1}FILE* fp2 = fopen("b.txt", "w");if (NULL == fp2){perror("open");return -1;		//在main函数中,按照程序开发的一般惯例,成功则返回0,失败则返回-1}char ch;while (!feof(fp1)){ch = fgetc(fp1);fputc(ch, fp2);printf("%d\t", ch);}fclose(fp1);fclose(fp2);return 0;
}

输出:

97      98      99      -1      -2      100     -1

文件结束符-1也会被写进去

如果不想写进去那么必须在feof函数调用之前先读一次,如:

#define _CRT_SECURE_NO_WARNINGS
#includeint main() {FILE* fp1 = fopen("a.txt", "r");if (NULL == fp1){perror("open");return -1;		//在main函数中,按照程序开发的一般惯例,成功则返回0,失败则返回-1}FILE* fp2 = fopen("b.txt", "w");if (NULL == fp2){perror("open");return -1;		//在main函数中,按照程序开发的一般惯例,成功则返回0,失败则返回-1}char ch;while (1){ch = fgetc(fp1);if (feof(fp1))break;fputc(ch, fp2);printf("%d\t", ch);}fclose(fp1);fclose(fp2);return 0;
}

输出

97      98      99      -1      -2      100

上面两个程序的区别在于,前者是先判断后读,后者是先读后判断。

6 使用ftell验证feof的行为

为什么先读后判断,和先判断后读,会出现上面的差别?

好的,我们现在就来找原因
现有一个名为a.txt的文件,内容如下:
在这里插入图片描述
现在使用先判断后读的方式,查看光标的变化,设计如下程序

#define _CRT_SECURE_NO_WARNINGS
#includevoid test_feof1() {FILE* fp1 = fopen("a.txt", "r");char ch;long i = 0;while (!feof(fp1)){i = ftell(fp1);printf("%d\t", i);ch = fgetc(fp1);i = ftell(fp1);printf("%d\t", i);printf("%d\n", ch);}fclose(fp1);
}int main() {test_feof1();return 0;
}

输出

0       1       51
1       2       43
2       3       53
3       4       61
4       5       56
5       7       10
7       7       -1

最后读到的字符是\n(ASCII码为10),接下来进行了下一轮的循环,随后fgetc读取失败返回-1,当读取换行符\n后文件光标向后移动两格(后面会介绍),即从从位置5到位置7。读完\n后,feof仍然认为文件没有结束,否则最后一轮循环就不会执行,因此上面的猜测是错误的。

现在使用先读后判断的方式,查看光标的变化

void test_feof2() {FILE* fp1 = fopen("a.txt", "r");char ch;long i;while (1){i = ftell(fp1);printf("%d\t", i);ch = fgetc(fp1);i = ftell(fp1);printf("%d\t", i);if (feof(fp1))break;printf("%d\n", ch);}return 0;
}
int main() {//test_feof();test_feof2();return 0;
}

输出:

0       1       51
1       2       43
2       3       53
3       4       61
4       5       56
5       7       10
7       7

现在可以得到结论了:feof函数什么时候判断文件结尾,只有fputc读取失败(返回-1)后,这个函数才会判定文件结束,哪怕是读完最后一个字符也不行,必须fputc读取失败才行。

至此,先读后判断和先判断后读的差别,原因也找到了。

7 feof不要和fgets配合使用,建议只和fgetc配合使用

使用函数feof判断文件是否读取结束,与fgetc和fgets两种读取函数配合时的表现是不一样的。当feof与fgetc配合使用时,fgetc只有读取失败返回-1之后,feof才认为文件读取结束,而feof与fgets配合时,只需要最后一行读完,feof就认为文件读取结束,无需等到读取失败。

我们来看一下feof与fgets配合使用时的的情形。新建一个名为test_line.txt的文件,内容如下:
在这里插入图片描述
新建一个统计行数(文件有多少行)的函数,内容如下:

#define _CRT_SECURE_NO_WARNINGS
#includeint line_number1()
{FILE* fp = fopen("./test_line.txt", "r");if (!fp){perror("open");exit(0);}int i = 0;char temp[256];while (1){fgets(temp, sizeof(temp), fp);if (feof(fp)){fclose(fp);return i;}i++;}
}

main函数如下:

int main()
{int lines1;lines1 = line_number1();printf("lines1=%d\n", lines1);
}

输出

lines1=2

while循环只执行了两次,因为读完第三行之后,feof就认为文件读取结束,后面的i++没来得及执行,就退出了循环。
如果把i++移动到if语句前面,也是不行的,因为当文件是一个空文件时,i++也会执行,会造成即使是空文件,其行数也为1。

那么当使用fgets读取文件时,如何判定读取结束呢?答案是使用fgets的返回值,fgets读到文件尾或出错返回的是NULL,因此可以用返回值是否为NULL判定是否读取结束。代码如下:

int line_number2()
{FILE* fp = fopen("./test_line.txt", "r");if (!fp){perror("open");exit(0);}int i = 0;char temp[256];char* p = NULL;while (1){p = fgets(temp, sizeof(temp), fp);if (NULL == p){fclose(fp);return i;}i++;}
}

main函数如下:

int main()
{int lines1, lines2;lines1 = line_number1();//printf("lines1=%d\n", lines1);lines2 = line_number2();printf("lines1=%d  lines2=%d\n", lines1, lines2);}

输出

lines1=2  lines2=3

结果符合要求。

8 关于Windows中换行被当成两个字符的问题

现在还有一个疑问,为什么读到换行符时,光标会向后移动两格?
新建b.txt,内容如下:
在这里插入图片描述

void test_n() {FILE* fp1 = fopen("b.txt", "r");char ch;long i;while (1){i = ftell(fp1);printf("%d\t", i);ch = fgetc(fp1);i = ftell(fp1);printf("%d\t", i);if (feof(fp1))break;printf("%d\n", ch);}
}
int main() {//test_feof();//test_feof2();test_n();return 0;
}

输出:

0       1       97
1       3       10
3       5       10
5       6       98
6       8       10
8       10      10
10      10

因为在Windows中,从内存往磁盘存储时,换行符被当成两个字符处理,因此在读取的时候,使用fgetc读是只读到一个字符,但光标却是移动两格
在这里插入图片描述
这个不用深究,只需要知道在Windows系统读取磁盘中的存在这种现象就行。

相关内容

热门资讯

监控摄像头接入GB28181平... 流程简介将监控摄像头的视频在网站和APP中直播,要解决的几个问题是:1&...
Windows10添加群晖磁盘... 在使用群晖NAS时,我们需要通过本地映射的方式把NAS映射成本地的一块磁盘使用。 通过...
protocol buffer... 目录 目录 什么是protocol buffer 1.protobuf 1.1安装  1.2使用...
在Word、WPS中插入AxM... 引言 我最近需要写一些文章,在排版时发现AxMath插入的公式竟然会导致行间距异常&#...
【PdgCntEditor】解... 一、问题背景 大部分的图书对应的PDF,目录中的页码并非PDF中直接索引的页码...
Fluent中创建监测点 1 概述某些仿真问题,需要创建监测点,用于获取空间定点的数据࿰...
educoder数据结构与算法...                                                   ...
MySQL下载和安装(Wind... 前言:刚换了一台电脑,里面所有东西都需要重新配置,习惯了所...
修复 爱普生 EPSON L4... L4151 L4153 L4156 L4158 L4163 L4165 L4166 L4168 L4...
MFC文件操作  MFC提供了一个文件操作的基类CFile,这个类提供了一个没有缓存的二进制格式的磁盘...