一、文件读取结束的判定
当我们使用例如fgetc、fgets、fscanf、fread等函数来读取文件内容时,我们可能遇到需要判断文件读取的结束,一般情况下都是通过这些函数的返回值来判断文件读取是否结束。
1、fgetc
返回读取的字符的ASCII值,如果读到文件末尾或发生错误,则返回EOF
(通常定义为-1)。
所以在使用fgetc函数读取文件内容时,要判断文件读取的结束则判断fgetc函数的返回值是否是EOF,如果fgetc函数的返回值为EOF,则可能是达到文件结尾,也就是文件读取结束。
例如使用fgetc以一个个字符读取文件的所有内容并打印出来:
cpp
#include <stdio.h>
int main()
{
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen");
return -1;
}
char ch = 0;
while ((ch = fgetc(pf)) != EOF)//使用fgetc函数的返回值作为判断,如果是EOF,则可能是读取结束
{
printf("%c ", ch);
}
printf("\n");
fclose(pf);
pf = NULL;
return 0;
}
文件内容为:

运行结果:

2、fgets
返回指向读取字符串的指针,如果读到文件末尾或发生错误,则返回NULL
。
如果fgets函数的返回值为NULL,则可能是达到文件结尾,也就是文件读取结束。
例如尝试使用fgets函数将文件的内容一行行读取并打印:
cpp
#include <stdio.h>
int main()
{
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen");
return -1;
}
char str[100] = { 0 };
while (fgets(str,100,pf) != NULL)//使用fgets函数的返回值作为判断,如果是NULL,则可能是读取结束
{
printf("%s", str);
}
fclose(pf);
pf = NULL;
return 0;
}
文件内容:

运行结果:

3、fscanf
返回成功匹配和赋值的输入项数,如果读到文件末尾或发生错误,则返回EOF
。
如果fscanf函数的返回值为EOF,则可能是达到文件结尾,也就是文件读取结束。
使用fscanf函数格式化读取文件中所有数据:
cpp
#include <stdio.h>
int main()
{
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen");
return -1;
}
int id = 0;
char name[10] = { 0 };
int age = 0;
while (fscanf(pf, "%d %s %d", &id, name, &age) == 3)//使用fscanf的返回值作为判定条件,如果返回值不等于期望匹配项,则可能是文件读取完成
{
printf("%d %s %d\n", id, name, age);
}
fclose(pf);
pf = NULL;
return 0;
}
文件内容:

运行结果:

4、fread
函数原型:
cpp
size_t fread(void *ptr, size_t size, size_t count, FILE *stream);
返回成功读取的元素个数,如果读到文件末尾或发生错误,则返回值可能小于count
。
文件内容,使用下面的程序写入test.bin文件中:
cpp
#include <stdio.h>
int main()
{
FILE* pf = fopen("test.bin", "wb");
if (pf == NULL)
{
perror("fopen");
return -1;
}
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
fwrite(arr, sizeof(int), sizeof(arr) / sizeof(int), pf);
fclose(pf);
pf = NULL;
return 0;
}
使用二进制编辑器打开查看(这里是小端字节序):

使用fread读取十个整形数据:
cpp
#include <stdio.h>
int main()
{
FILE* pf = fopen("test.bin", "rb");
if (pf == NULL)
{
perror("fopen");
return -1;
}
int temp = 0;
while (fread(&temp, sizeof(int), 1, pf) == 1)//以fread函数的返回值为判定,因为这里读取元素个数为1,正确读取应当返回1,如果返回值不是1,则可能是文件结尾,也就是文件读取完成
{
printf("%d ", temp);
}
printf("\n");
fclose(pf);
pf = NULL;
return 0;
}
运行结果:

5、总结
- 对于
fgetc
,当返回值为EOF
时,可能表示文件读取结束。 - 对于
fgets
,当返回值为NULL
时,可能表示文件读取结束。 - 对于
fscanf
,当返回值为EOF
时,可能表示文件读取结束。 - 对于
fread
,当返回值小于请求的元素个数count
时,可能表示文件读取结束。
二、feof和ferror
在上面的文件读取结束的判定中,只是初步的判定,因为对于那些读取文件的函数的特殊返回值不一定是代表文件到了结尾(文件读取结束),也可能是代表文件读取错误。所以我们需要判断是哪一种情况,到底是文件读取结束还是文件读取错误,就需要下面的两个函数。
1、feof
feof
函数用于检测文件的结束标志(End-Of-File)。
函数原型:

函数参数和返回值:
参数 stream
是文件流指针,当文件指针到达文件末尾时,feof
函数返回非零值(通常是1),否则返回0。
2、ferror
ferror
函数用于检测文件操作是否发生错误。
函数原型:

函数参数和返回值:
参数 stream
是文件流指针,如果在文件操作过程中发生错误,ferror
函数返回非零值(通常是1),否则返回0。
3、使用示例:
如果需要区分文件结束和读取错误,可以使用feof
和ferror
函数。
test.txt文件内容:

下面的代码尝试以一个个字符读取此文件的全部内容,并且打印出来:
cpp
#include <stdio.h>
int main() {
FILE* pf = fopen("test.txt", "r");
if (pf == NULL) {
perror("fopen");
return 0;
}
char ch = 0;
while ((ch = fgetc(pf)) != EOF) {
printf("%c ", ch);
}
printf("\n");
if (feof(pf)) {
printf("文件读取结束\n");
}
else if (ferror(pf)) {
printf("文件读取错误\n");
}
fclose(pf);
pf = NULL;
return 0;
}
运行结果:

在上述代码中,feof(file)
用于检查是否到达文件末尾,而ferror(file)
用于检查是否发生了读取错误。这里的结果是文件读取结束。