C语言使用fscanf读取CSV文件无法进入while循环问题排查
核心故障点
输出缓冲区未刷新导致的误判
循环内的printf("Enters the while loop")没有添加换行符\n,标准输出stdout默认使用行缓冲策略,只有遇到换行符、缓冲区占满或者程序退出时才会把内容打印到终端。大概率你的循环已经正常执行,只是输出内容滞留在缓冲区未显示,导致你误以为没有进入循环。
修复方式:修改为printf("Enters the while loop\n");即可验证。参数校验顺序完全错误
你先执行fopen(argv[1])读取文件,之后才判断argc<2。如果程序运行时没有传入CSV路径参数,会直接访问空指针argv[1],导致fopen失败触发assert异常。即使你当前传入了参数可以正常打开文件,这个逻辑漏洞也必须修复。
修复方式:把argc<2的判断移到读取argv[1]、打开文件的操作之前。格式串未处理行尾换行符
你当前的格式串"%[^,],%[^,],%d,%[^,],%lf,%lf"读完最后一个double字段后,行尾的换行符会残留在文件缓冲区中,下一次循环读取第一个字段时,%[^,]会把换行符作为首字符读取,后续所有字段匹配错位,fscanf返回值不为6,循环直接终止。
修复方式:格式串末尾添加换行符,修改为"%[^,],%[^,],%d,%[^,],%lf,%lf\n",自动吃掉每行末尾的换行。重定义size_t触发未定义行为
标准库头文件stdlib.h已经定义了size_t类型,你手动添加的typedef unsigned long size_t;会触发类型重定义冲突,导致程序运行逻辑异常,严重时会直接崩溃。
修复方式:删除你自定义size_t的那一行代码即可。CSV存在表头行导致匹配失败
如果你的CSV文件第一行是字段名表头(比如wt_id,postcode,pop_served,wt_manager,x,y这类字符串),fscanf尝试从表头行读取int、double类型字段时会直接匹配失败,返回值远小于6,循环根本不会执行。
修复方式:进入循环前先读取并丢弃第一行表头,比如加一行fscanf(file, "%*[^\n]\n");跳过首行。
额外注意点
- 你使用的
%[^,]格式符没有限制读取长度,一旦CSV中某个字符串字段长度超过127字节,会直接溢出结构体的字符数组,破坏内存数据。可以修改为%127[^,]限制最多读取127个字符,留1位存字符串结束符\0。 - 如果CSV中存在空字段(两个逗号相邻的
,,场景),%[^,]会匹配失败,这种场景更推荐先用fgets读取整行,再用strtok拆分字段处理,鲁棒性更高。
修复后参考代码
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <assert.h> typedef struct { char wt_id[128]; char postcode[128]; int pop_served; char wt_manager[128]; double x; double y; } watchtower_t; int main(int argc, char **argv) { watchtower_t temp; // 先校验参数再操作文件 if(argc < 2) { printf("请传入CSV文件路径\n"); return 1; } char *filename = argv[1]; FILE *file = fopen(filename, "r"); assert(file); // 如果有表头就放开下一行注释 // fscanf(file, "%*[^\n]\n"); while(fscanf(file, "%127[^,],%127[^,],%d,%127[^,],%lf,%lf\n", temp.wt_id, temp.postcode, &temp.pop_served, temp.wt_manager, &temp.x, &temp.y) == 6) { printf("进入循环,当前wt_id:%s\n", temp.wt_id); } fclose(file); return 0; }
内容的提问来源于stack exchange,提问作者Aquila。

