C语言文件处理疑问:以1字节读取文件时大小计算为何不一致?
你的问题主要来自两个核心点:文本模式的换行转换和**feof()的逻辑误用**,咱们逐个拆解:
1. 文本模式导致字节数与读取字符数不匹配
你用"r"模式打开文件,这属于文本模式。在Windows系统中,文本模式下会自动把文件里的\r\n(占2个字节的Windows换行符)转换成单个\n字符(占1个字节的Unix风格换行)。
你通过fseek+ftell得到的size=78是文件的实际磁盘字节数,但在文本模式读取时,每遇到一组\r\n就会少读1个字节。如果你的文件里正好有2组这样的换行,实际读取的字符数就会是78-2=76,这和你第二次输出的结果完全对应。
2. feof()的常见误区
feof()的作用不是“预判是否到文件末尾”,而是判断上一次读取操作是否因为到达末尾而失败。你的循环条件!feof(file)会导致循环多执行一次:
当读完最后一个有效字符时,feof()仍然返回假,循环会继续执行。此时fread()读取失败(返回0),但你还是执行了size = i + 1——不过在你的案例里,这个问题被文本模式的换行转换掩盖了,因为实际读取的字符数本身就比字节数少。
针对这两个问题,你可以这样修改代码:
方案一:用二进制模式打开文件
如果你的文件是二进制文件,或者你需要精确读取所有字节,把打开模式改成"rb":
errno_t err = fopen_s(&file,"f.txt","rb");
二进制模式不会进行任何换行转换,ftell()得到的字节数和实际读取的字节数会完全一致。
方案二:用fread()返回值判断循环
不管是文本还是二进制模式,都应该依赖fread()的返回值判断是否读取成功,而不是feof():
int i = 0; size_t read_count; while ((read_count = fread(tmp + i, 1, 1, file)) == 1) { i++; } size = i;
这样能准确统计实际读取的字符数,避免feof()带来的循环多执行问题。
完整优化后的代码
#include <stdio.h> #include <stdlib.h> int main(void) { FILE * file; errno_t err = fopen_s(&file,"f.txt","rb"); // 二进制模式打开 if (err != 0) { // 必须检查文件是否成功打开 printf("Failed to open file\n"); return 1; } fseek(file, 0, SEEK_END); long size = ftell(file); // 用long存储更安全,避免int溢出 fseek(file, 0, SEEK_SET); char *tmp = malloc(size); if (tmp == NULL) { // 检查内存分配是否成功 printf("Memory allocation failed\n"); fclose(file); return 1; } printf("%ld\n", size); int i = 0; size_t read_count; while ((read_count = fread(tmp + i, 1, 1, file)) == 1) { i++; } size = i; printf("%d\n", size); fclose(file); free(tmp); return 0; }
额外提醒:一定要检查fopen_s()和malloc()的返回值,避免空指针导致程序崩溃;ftell()返回的是long类型,用int存储可能会溢出,建议用long处理文件大小。
内容的提问来源于stack exchange,提问作者chiezs

