fscanf读取输入文件最后一行(仅负值场景?)失败问题排查求助
问题定位与修复方案
根因分析
- 最后一行末尾无换行符导致提前终止循环:你使用
fscanf("%f%c")要求每次必须匹配1个浮点数+1个字符才返回2,若文件最后一行末尾没有换行符,读完最后一个浮点数就到达EOF,fscanf返回1,你直接执行break跳出循环,此时已经读取到的最后几个浮点数还未存入结果向量,直接被丢弃,这就是结果时有时无的核心原因——不同编辑器保存文件时,是否自动在末尾加换行符的规则不同。 - 格式匹配逻辑不严谨:文件中浮点数的分隔符是逗号,你当前逻辑误将逗号读入字符变量c,虽然
c!='\n'的判断刚好能跳过逗号,但逻辑完全不可靠,同时coordinate!='\n'是无效判断,浮点数不可能等于换行符的ASCII值。 - 变量名笔误(潜在崩溃风险):代码中定义指针变量时写的是
double** vex = ...,后续断言和使用时用的是vecs,属于笔误,会触发未定义行为,大概率直接崩溃。 - 格式符不匹配:用
%f读取float再赋值给double类型的数组元素,存在不必要的精度损失。
修复后代码
typedef struct vectors { double** vecs; int n; /* number of vectors */ } vectors; vectors* parseInput(char* file_name, int dim) { double coordinate; // 改double直接存,不需要float中转 char c; double *vec = NULL; int i=0, j=0, debug; FILE* input; vectors* datapoints; // 修正变量名笔误,vex改成vecs double** vecs = (double**) calloc (1000, sizeof (double*)); // 不需要乘dim,1000是最大向量数 assert(vecs != NULL); datapoints = (vectors*)malloc(sizeof(vectors)); assert(datapoints != NULL); input = fopen(file_name, "r"); assert(input != NULL); while (1) { // 先读浮点数,用%lf匹配double debug = fscanf(input, "%lf", &coordinate); if(debug == EOF) { break; } if (debug != 1) { // 非法格式直接报错跳出 fprintf(stderr, "Invalid file format\n"); break; } // 新向量初始化 if (j == 0) { vec = (double *)calloc(dim, sizeof(double)); assert(vec != NULL); } // 存入当前坐标 vec[j] = coordinate; j++; // 读完一个完整向量就存入结果集 if (j == dim) { print_vec(vec, dim); vecs[i] = vec; i++; j = 0; } // 跳过后面所有的分隔符:逗号、空格、换行、回车 while((c = fgetc(input)) != EOF && (c == ',' || c == ' ' || c == '\n' || c == '\r')); if (c != EOF) { // 把多读的非分隔符塞回流里,给下一次读浮点数用 ungetc(c, input); } } // 收缩内存 vecs = (double**)realloc(vecs, i*sizeof(double*)); assert(vecs != NULL); datapoints->vecs = vecs; datapoints->n = i; fclose(input); return datapoints; }
修改说明
- 修正了变量名笔误,同时调整vecs的初始分配大小,原本乘dim属于多余操作,1000是支持的最大向量数量,可根据需求调整。
- 拆分读取逻辑,先单独读取浮点数,再跳过所有分隔符(逗号、空格、换行、回车),不受最后一行是否有换行符的影响。
- 替换
%f为%lf直接读取double类型,避免精度损失。 - 删除无效判断逻辑,代码健壮性更高。
内容的提问来源于stack exchange,提问作者Itai Zemah
相关产品推荐
相关产品推荐

