C语言从文件读取多整数与字符串到双向链表的问题排查
看起来你踩了文件读取和内存管理里的两个典型坑,咱们一步步拆解解决:
问题1:所有节点的Description被最后一条数据覆盖
这个问题的核心是内存复用导致指针指向同一缓冲区。你大概率是用了一个固定的char数组(比如char desc_buf[256];)临时存储读取到的Description,然后直接把节点的Description指针指向这个数组。这样所有节点的Description都指向同一块内存区域,最后一次写入的内容会覆盖之前所有的,自然所有节点显示的都是最后一条的Description。
解决方案:为每个节点的Description分配独立内存
每次读取到Description字符串后,为当前节点单独分配内存并复制字符串。可以用strdup()(内部自动完成malloc+strcpy),或者手动用malloc()+strcpy():
// 假设读取到的临时字符串存在buf中 task->Description = strdup(buf); // 或者手动分配内存(更可控): task->Description = malloc(strlen(buf) + 1); if (task->Description == NULL) { perror("malloc failed"); exit(EXIT_FAILURE); } strcpy(task->Description, buf);
注意:销毁节点时记得free这个Description指针,避免内存泄漏。
问题2:链表最后一个元素重复
这个问题通常是feof()误用或者fscanf与fgets配合时的缓冲区残留导致的:
常见原因1:用while(!feof(fp))作为循环条件
feof()是在尝试读取文件末尾之后才会返回true,所以当你读取到最后一条记录后,循环会再执行一次——此时fscanf读取失败,但你可能还是创建了节点并插入,导致重复(或用上一次的残留值)。
常见原因2:fscanf读取整数后,缓冲区残留换行符
fscanf读取整数时会跳过空白字符,但不会读取最后的换行符。如果接着用fgets读取Description,第一次fgets会读取到这个换行符(空字符串),后续读取逻辑混乱,最终导致重复插入最后一条记录。
解决方案:正确控制循环条件并处理缓冲区
根据你的文件格式,每条记录包含12个整数+1个字符串,建议把fscanf读取整数的返回值作为循环条件,同时用fscanf直接读取字符串(你的Description无空格,适合用%s):
FILE *fp = fopen("your_file.txt", "r"); if (fp == NULL) { perror("fopen failed"); exit(EXIT_FAILURE); } int ID, Priority, Status; int c1, c2, c3; // creation日期的三个字段 int d1, d2, d3; // deadline日期的三个字段 int co1, co2, co3; // conclusion日期的三个字段 char desc_buf[256]; // 用fscanf返回值判断是否成功读取12个整数 while (fscanf(fp, "%d %d %d %d %d %d %d %d %d %d %d %d", &ID, &Priority, &Status, &c1, &c2, &c3, &d1, &d2, &d3, &co1, &co2, &co3) == 12) { // 读取Description(无空格,用%s直接读取) if (fscanf(fp, "%s", desc_buf) != 1) { fprintf(stderr, "Failed to read description\n"); break; } // 创建新节点 Task new_task = malloc(sizeof(Task_node2)); if (new_task == NULL) { perror("malloc failed"); exit(EXIT_FAILURE); } // 填充节点数据 new_task->ID = ID; new_task->Priority = Priority; new_task->Status = Status; // 填充Date字段(假设你已有创建Date结构体的逻辑) new_task->creation = create_date(c1, c2, c3); new_task->deadline = create_date(d1, d2, d3); new_task->conclusion = create_date(co1, co2, co3); // 为Description分配独立内存 new_task->Description = strdup(desc_buf); // 初始化双向链表指针 new_task->next = NULL; new_task->previous = NULL; // 插入到双向链表(复用你已有的插入函数) insert_task_to_list(new_task); } fclose(fp);
如果你的Description可能包含空格,就不能用%s,需要先跳过fscanf后的换行符,再用fgets读取:
// 读取完12个整数后,跳过缓冲区中的空白字符(包括换行) while (fgetc(fp) != '\n' && !feof(fp)); // 读取Description并去掉末尾换行符 if (fgets(desc_buf, sizeof(desc_buf), fp) != NULL) { desc_buf[strcspn(desc_buf, "\n")] = '\0'; new_task->Description = strdup(desc_buf); }
额外调试建议
- 每次读取后打印当前的ID和Description,确认每一步数据是否正确;
- 检查链表插入函数的逻辑,确保不会在插入时重复添加节点。
内容的提问来源于stack exchange,提问作者First Watcher of the Shores

