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

将CSV读取到结构体数组时遇段错误及解析失败问题求助

问题修复方案

核心问题分析

你的代码存在两个致命问题,直接导致段错误和解析失败:

  1. 野指针未初始化:
    • char *line; 是未分配内存的野指针,fgets 尝试向非法地址写入数据触发段错误。
    • 结构体中的 name、dept 是未初始化的指针,sscanf 无法向有效内存写入字符串,进一步引发内存混乱。
  2. 解析逻辑依赖非法内存:即使格式串正确,野指针导致的内存错误会让解析行为完全不可预测。

修复后的完整代码

方案一:使用固定大小数组(简单直接,适合已知字段长度场景)

#define MAXROW 30
#define MAX_STR_LEN 30  // 定义字符串字段的最大长度
#define LINE_BUF_SIZE 256  // 每行CSV的最大缓冲区

typedef struct emp_info {
    int emp_id;
    char name[MAX_STR_LEN];  // 替换指针为固定数组
    char dept[MAX_STR_LEN];
    int salary;
} emp_info;

void store(emp_info *data, char *filename);

int main(int argc, char **argv)
{
    emp_info data[MAXROW];
    char *filename = "dat/emp_info.csv";
    store(data, filename);
    return 0;
}

void store(emp_info emp_arr[], char *filename)
{
    size_t n = 0;
    char line[LINE_BUF_SIZE];  // 分配足够的栈内存存储每行数据
    FILE *csv_stream;
    
    if ((csv_stream = fopen(filename, "r")) == NULL) {
        fprintf(stderr, "file open for reading failed. exit!\n");
        exit(EXIT_FAILURE);
    }

    while (n < MAXROW && fgets(line, sizeof(line), csv_stream) != NULL) {
        // 去掉行尾的换行符(如果存在),避免干扰解析
        line[strcspn(line, "\n")] = '\0';
        
        emp_info tmp;
        // 使用%s匹配字符串,结合数组长度限制避免溢出
        if (sscanf(line, "%d,%29[^,],%29[^,],%d",
                   &tmp.emp_id, tmp.name, tmp.dept, &tmp.salary) != 4) {
            fprintf(stderr, "error: failed to parse line %zu.\n", n+1);
            exit(EXIT_FAILURE);
        }
        emp_arr[n++] = tmp;
    }

    if (fclose(csv_stream) == EOF)
        perror("stream error-csv_stream.");
}

方案二:动态分配内存(灵活适配可变长度字段)

如果需要支持更长的字符串字段,可以用动态分配内存:

#define MAXROW 30
#define LINE_BUF_SIZE 256

typedef struct emp_info {
    int emp_id;
    char *name;
    char *dept;
    int salary;
} emp_info;

void store(emp_info *data, char *filename);
void free_emp_data(emp_info *data, size_t count);  // 新增释放内存函数

int main(int argc, char **argv)
{
    emp_info data[MAXROW];
    char *filename = "dat/emp_info.csv";
    store(data, filename);
    
    // 使用完数据后记得释放内存
    free_emp_data(data, MAXROW);
    return 0;
}

void store(emp_info emp_arr[], char *filename)
{
    size_t n = 0;
    char line[LINE_BUF_SIZE];
    FILE *csv_stream;
    
    if ((csv_stream = fopen(filename, "r")) == NULL) {
        fprintf(stderr, "file open for reading failed. exit!\n");
        exit(EXIT_FAILURE);
    }

    while (n < MAXROW && fgets(line, sizeof(line), csv_stream) != NULL) {
        line[strcspn(line, "\n")] = '\0';
        
        emp_info tmp;
        char name_buf[LINE_BUF_SIZE], dept_buf[LINE_BUF_SIZE];
        // 先读取到临时缓冲区,再动态分配内存
        if (sscanf(line, "%d,%[^,],%[^,],%d",
                   &tmp.emp_id, name_buf, dept_buf, &tmp.salary) != 4) {
            fprintf(stderr, "error: failed to parse line %zu.\n", n+1);
            exit(EXIT_FAILURE);
        }
        // 分配足够内存并复制字符串
        tmp.name = malloc(strlen(name_buf) + 1);
        tmp.dept = malloc(strlen(dept_buf) + 1);
        if (!tmp.name || !tmp.dept) {
            fprintf(stderr, "memory allocation failed.\n");
            exit(EXIT_FAILURE);
        }
        strcpy(tmp.name, name_buf);
        strcpy(tmp.dept, dept_buf);
        
        emp_arr[n++] = tmp;
    }

    if (fclose(csv_stream) == EOF)
        perror("stream error-csv_stream.");
}

void free_emp_data(emp_info *data, size_t count)
{
    for (size_t i = 0; i < count; i++) {
        free(data[i].name);
        free(data[i].dept);
    }
}

关键修改说明

  1. 修复野指针:
    • 将 char *line 改为栈数组 char line[LINE_BUF_SIZE],确保有合法内存存储每行数据。
    • 方案一中把结构体的指针成员改为固定大小数组,直接提供存储区域;方案二中先读取到临时缓冲区,再动态分配内存存储字符串。
  2. 优化解析逻辑:
    • 使用 strcspn(line, "\n") 去掉行尾的换行符,避免换行符干扰 sscanf 的匹配。
    • 格式串中 %29[^,] 限制读取的字符数,防止数组溢出(数组大小为30,留1个位置给终止符\0)。
  3. 内存管理:方案二中新增了内存释放函数,避免内存泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 00:06:23