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

CSV文件最后一行解析异常问题排查与修复咨询

解析CSV时循环重复执行的问题分析与修复

问题背景

CSV文件内容

name,birthdate,phone
Bob smith,1111-11-11,222-333-3333
Mary brown,2222-22-22,333-444-4444
Billy Bob,2001-01-11,222-333-4444

原解析代码

int loadCSV(char *filename) {
    FILE *x = fopen(filename, "rt");
    if (x == NULL)
        exit(1);
    char record[1000];
    char field1[100], field2[100], field3[100];
    fgets(record, 999, x);
    fgets(record, 999, x);
    while (!feof(x)) {
        int rec = 0;
        int i;
        for (i = 0; record[rec] != '\0' && i < 999 && record[rec] != ','; i++, rec++) {
            field1[i] = record[rec];
        }

        field1[i] = '\0';
        rec++;
        for (i = 0; record[rec] != '\0' && i < 999 && record[rec] != ','; i++, rec++) {
            field2[i] = record[rec];
        }    
        field2[i] = '\0';
        rec++;
        for (i = 0; record[rec] != '\0' && i < 999 && record[rec] != ',' && record[rec] != '\n' ; i++, rec++) {
            field3[i] = record[rec];
        }
        field3[i] = '\0';
        printf("%s %s %s\n", field1, field2, field3);
        fgets(record, 999, x);
    }
    return 0;
}

期望输出

Bob smith,1111-11-11,222-333-3333

Mary brown,2222-22-22,1-333-444-4444

Billy Bob,2001-01-11,222-333-4444

实际输出

Bob smith, 1111-11-11, 222-333-3333

Mary brown,2222-22-22,1-333-444-4444

Billy Bob,2001-01-11,222-333-4444

Billy Bob

最小复现代码

int loadCSV(char *filename) {
    char record[1000];
    char field1[100], field2[100], field3[100];
    FILE *x = fopen(filename, "rt");
    if (x == NULL)
        exit(1);
    
    fgets(record, 999, x);
    fgets(record, 999, x);
    
    while (!feof(x)) {
        int rec = 0;
        int i;
    
        for (i = 0; record[rec] != '\0' && i < 999 && record[rec] != ','; i++, rec++) {
            field1[i] = record[rec];
        }
        field1[i] = '\0';
        rec++;
    
        printf("%s %s %s\n", field1, field2, field3);
        fgets(record, 999, x);
    }
}

问题原因

  1. while (!feof(x))的逻辑缺陷:
    feof函数只有在尝试读取并越过文件末尾后才会返回真。当读取到最后一行后,执行fgets(record, 999, x)会失败,但此时feof(x)仍为假,循环会继续执行一次。此时record中保留的是上一次读取的内容(Billy Bob那一行),所以会重复解析并输出field1的内容。

  2. 最小复现中的字段未完整解析:
    最小复现代码只完成了field1的解析,field2和field3未被赋值,属于未初始化的内存,打印时会输出垃圾值;同时因为上述feof的问题,最后一次循环只会输出上一次的field1内容。

  3. 原代码的其他潜在问题:

    • 字段复制时i < 999的限制不合理:field数组长度仅为100,应该限制i < 99,避免数组溢出。
    • 直接rec++未做边界检查:如果某行末尾没有逗号,rec可能越界访问record数组。

修复建议

1. 替换循环条件,用fgets的返回值判断

把while (!feof(x))改为while (fgets(record, 999, x) != NULL),这样只有当成功读取到一行时才进入循环,从根源避免最后一次无效循环。

2. 正确跳过表头

原代码用两次fgets跳表头,若表头行长度超过998会导致读取不完整,建议改为:

// 跳过表头
if (fgets(record, 999, x) == NULL) {
    fclose(x);
    return 1;
}

3. 完善字段解析的边界控制

  • 限制字段复制时的i不超过字段数组的最大可用长度(比如i < 99,因为数组长度100,留一个位置给终止符'\0')。
  • 解析时检查rec是否超出record的范围,避免越界。

4. 初始化字段数组

避免未初始化的垃圾值被打印,在定义字段数组时初始化:

char field1[100] = "", field2[100] = "", field3[100] = "";

修复后的完整代码示例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int loadCSV(char *filename) {
    FILE *x = fopen(filename, "rt");
    if (x == NULL) {
        perror("Failed to open file");
        exit(EXIT_FAILURE);
    }
    char record[1000];
    char field1[100] = "", field2[100] = "", field3[100] = "";
    
    // 跳过表头
    if (fgets(record, 999, x) == NULL) {
        fclose(x);
        return 1;
    }

    // 用fgets返回值作为循环条件
    while (fgets(record, 999, x) != NULL) {
        int rec = 0;
        int i;

        // 解析field1
        for (i = 0; record[rec] != '\0' && i < 99 && record[rec] != ','; i++, rec++) {
            field1[i] = record[rec];
        }
        field1[i] = '\0';
        // 跳过逗号,若已到行尾则停止
        if (record[rec] == ',') rec++;

        // 解析field2
        for (i = 0; record[rec] != '\0' && i < 99 && record[rec] != ','; i++, rec++) {
            field2[i] = record[rec];
        }
        field2[i] = '\0';
        if (record[rec] == ',') rec++;

        // 解析field3,处理换行符
        for (i = 0; record[rec] != '\0' && i < 99 && record[rec] != '\n'; i++, rec++) {
            field3[i] = record[rec];
        }
        field3[i] = '\0';

        printf("%s,%s,%s\n\n", field1, field2, field3);
    }

    fclose(x);
    return 0;
}

内容的提问来源于stack exchange,提问作者Blue Apricot

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 17:03:12