You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言从文件读取多整数与字符串到双向链表的问题排查

解决双向链表读取文件时的重复节点与Description字段覆盖问题

看起来你踩了文件读取和内存管理里的两个典型坑,咱们一步步拆解解决:

问题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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 07:07:37