You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在C语言中将CSV文件数据读取到结构体数组中?

问题:CSV数据无法正确读取到结构体数组中

我正尝试将CSV文件中的数据读取到结构体数组中,程序已计算出所需结构体的数量并分配了足够的内存,但将数据扫描到数组的操作无法正常执行。相关代码片段如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct{
    char *date;
    double open;
    double high;
    double low;
    double close;
    double volume;
    double adjclose;
} DATA;
DATA *table, *highest_table;
int main(int argc, char *argv[]) {
    // 省略了计算行数和分配内存的代码
    // 以下是尝试读取数据的逻辑(示例)
    FILE *fp = fopen("data.csv", "r");
    if (!fp) { /* 简单错误处理 */ }
    for (int i = 0; i < count; i++) {
        fscanf(fp, "%s,%lf,%lf,%lf,%lf,%lf,%lf", 
               table[i].date, &table[i].open, &table[i].high, 
               &table[i].low, &table[i].close, &table[i].volume, 
               &table[i].adjclose);
    }
}

核心问题分析

扫描失败大概率是两个关键问题导致的:

  1. char *date是野指针:结构体里的date只是一个未初始化的指针,没有分配实际内存空间,直接用fscanf写入会触发内存访问错误,要么崩溃要么数据乱码。
  2. fscanf格式字符串不匹配CSV规则:如果日期是2024-05-20这类带特殊字符的字符串,%s会在遇到逗号前停止读取,但如果格式字符串没处理好分隔逻辑,或者CSV有表头没跳过,会直接导致后续数值读取错位。

修复后的完整代码

下面是解决了内存、格式匹配和错误处理问题的完整代码:

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

typedef struct{
    char *date;
    double open;
    double high;
    double low;
    double close;
    double volume;
    double adjclose;
} DATA;

DATA *table;

int main(int argc, char *argv[]) {
    // 1. 打开CSV文件并检查
    FILE *fp = fopen("data.csv", "r");
    if (!fp) {
        perror("Failed to open CSV file");
        return EXIT_FAILURE;
    }

    // 2. 统计有效数据行数(跳过表头)
    char buffer[256];
    int data_count = 0;
    // 先跳过第一行表头
    if (!fgets(buffer, sizeof(buffer), fp)) {
        fclose(fp);
        perror("Empty CSV file");
        return EXIT_FAILURE;
    }
    // 统计剩余数据行数
    while (fgets(buffer, sizeof(buffer), fp)) {
        data_count++;
    }
    // 重置文件指针到开头,再次跳过表头
    fseek(fp, 0, SEEK_SET);
    fgets(buffer, sizeof(buffer), fp);

    // 3. 分配结构体数组内存并检查
    table = malloc(data_count * sizeof(DATA));
    if (!table) {
        perror("Failed to allocate memory for struct array");
        fclose(fp);
        return EXIT_FAILURE;
    }

    // 4. 读取数据到结构体数组
    int i = 0;
    char date_temp[20]; // 临时存储日期,避免野指针
    while (fscanf(fp, "%[^,],%lf,%lf,%lf,%lf,%lf,%lf", 
                  date_temp, &table[i].open, &table[i].high, 
                  &table[i].low, &table[i].close, &table[i].volume, 
                  &table[i].adjclose) == 7) {
        // 为date字段分配独立内存并复制内容
        table[i].date = strdup(date_temp);
        if (!table[i].date) {
            perror("Failed to allocate memory for date string");
            // 出错时清理已分配资源,避免内存泄漏
            for (int j = 0; j < i; j++) {
                free(table[j].date);
            }
            free(table);
            fclose(fp);
            return EXIT_FAILURE;
        }
        i++;
    }

    // 5. 测试输出(可选)
    printf("Successfully read %d data entries:\n", data_count);
    for (int j = 0; j < data_count; j++) {
        printf("Entry %d: Date=%s, Open=%.2lf\n", 
               j+1, table[j].date, table[j].open);
        free(table[j].date); // 先释放date字段的内存
    }
    free(table); // 再释放结构体数组内存
    fclose(fp);

    return EXIT_SUCCESS;
}

关键修复点说明

  • 解决野指针问题:用临时缓冲区date_temp先读取日期,再通过strduptable[i].date分配独立内存,确保写入有合法的存储空间。
  • 匹配CSV格式%[^,]表示读取所有字符直到遇到逗号,完美适配CSV的逗号分隔规则,保证日期字符串被完整读取。
  • 跳过表头:CSV通常第一行是表头,必须跳过否则会把表头内容读入结构体,导致后续数值读取错位。
  • 完善错误处理:每一步操作(文件打开、内存分配、数据读取)都添加了错误检查,出错时会清理已分配的资源,避免内存泄漏。
  • 正确释放内存:使用完结构体数组后,要先逐个释放date字段的内存,再释放整个数组的内存,避免内存泄漏。

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

火山引擎 最新活动