从函数的名字来看,大家很容易把函数的功能理解成:检测是是否达到文件的结尾。但事实真的如此吗?我们首先来查阅feof函数的返回值:
所以可以确定的是,如果feof检测出文件结束志标记则返回一个非零值。但这里需要注意,feof检测的是 “文件结束标记” ,而不是 “文件位置标记” ,这在FILE结构体中属于两种不同的标记。文件到达结尾只是说明文件的位置标记达到末尾,不能说明文件结束标记的情况,而只有文件结束标记才能使feof的返回值为真。
我们可以用下面的代码来说明上面的结论,预先使test.txt的内容为空。
int main()
{
FILE* fp = fopen("test.txt", "r");
if (fp == NULL)
{
printf("%s", strerror(errno));
return 0;
}
for (int i = 0; i < 2; i++)
{
printf("feof() = %d\n", feof(fp));
fgetc(fp);
}
fclose(fp);
fp = NULL;
return 0;
}
打印结果如下:
feof() = 0;
feof() = 1;
可以看到即使到达了文件结尾,feof也不能立刻检测出。那为什么第二次就可以呢?其实原因在于fgetc发挥了作用。根据C标准对该函数的说明,当fgetc返回值为EOF时且确实已经到达文件结尾时(返回值为EOF可能有其他的情况),fgetc函数会自动设置文件结束标记。
所以换言之,feof函数并不能检测是否达到文件结尾,它只能测出文件读取失败是否由于文件读取结束引起的。文件读取失败对于fgetc函数来说表现在返回值为EOF。
正确的做法是根据函数的返回值来进行判断,现在我们对常用函数进行逐一说明:
fgetc函数需要检测返回值是否为EOF
如果到达文件结尾,文件结束标志被fgetc函数设置,可以通过feof函数检测出
如果发生读取错误,文件错误标志被fgetc函数设置,可以通过ferror函数检测出
int main()
{
FILE* fp = fopen("test.txt", "r");
if (fp == NULL)
{
printf("%s", strerror(errno));
return 0;
}
int c = 0;
while ((c = fgetc(fp)) != EOF)
{
putchar(c);
}
//判断是什么原因结束的
if (ferror(fp))
{
printf("I/O error when reading\n");
}
else if (feof(fp))
{
printf("End of file reached successfully\n");
}
flose(fp);
fp = NULL;
return 0;
}
fgets函数需要检测返回值是否为NULL
如果到达文件结尾,文件结束标志被fgets函数设置,可以通过feof函数检测出
如果发生读取错误,文件错误标志被fgets函数设置,可以通过ferror函数检测出
对于fread函数我们需要检测实际返回的读取个数和预期读取个数
如果实际读取个数和预期读取个数不同,说明要么发生读取错误,要么达到文件结尾,那么相应的文件错误标志和文件结束标志也被设置。可以通过ferror和feof函数进行检验。
#define SIZE 5
int main(void)
{
double src[SIZE] = { 1.0, 2.0, 3.0, 4.0, 5.0 };
double buf[SIZE] = {0};
FILE* fp = fopen("test.bin", "wb"); // 必须用二进制模式
fwrite(src, sizeof(src[0]), SIZE, fp); // 写 double 的数组
fclose(fp);
fp = fopen("test.bin", "rb");
size_t ret_code = fread(buf, sizeof(buf[0]), SIZE, fp); // 读 double 的数组
if (ret_code == SIZE)
{
puts("Array read successfully, contents: ");
for (int n = 0; n < SIZE; ++n)
printf("%f ", buf[n]);
putchar('\n');
}
else // error handling
{
if (feof(fp))
printf("Error reading test.bin: unexpected end of file\n");
else if (ferror(fp))
perror("Error reading test.bin");
}
fclose(fp);
fp = NULL;
return 0;
}
**fgetc、fgets、fread才是判断文件是否读取结束的,
- 如果fgetc返回EOF则文件读取结束,否则没结束返回当前文件位置标记的ASCII码值。
- 如果fgets返回NULL则文件读取结束,否则返回当前文件位置标记的地址
- 如果fread返回的实际值即读的实际个数小于预期读的个数说明读取结束,否则没有读取结束**
但是上面三种方式都只是判断文件是否读取结束,但是并不知道是正常读取结束还是异常导致的读取结束,所以我们要通过feof来判断,如果返回非0值,是正常读取结束,否则是异常读取结束,用ferror来打印错误信息。
小编制作不易,点个小猪猪,谢谢大家!