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

C语言如何读取带表头CSV列数据并存储到结构体中

核心问题修正点

原代码存在以下问题导致无法正常读取:

  • 结构体字段类型定义错误:分数为带小数的数值,原定义用char单字符类型无法存储5.5这类浮点数,也不支持字符串拷贝操作
  • 变量名不统一:定义了field变量,实际逻辑中使用未声明的campoCount,无法通过编译
  • 未处理表头行:第一行Aluno;p1;p2是列名而非学生数据,直接存入结构体会导致第一条数据无效
  • 字段计数器未逐行重置:每行分割后计数器没有归零,下一行的字段判断逻辑完全失效
  • 缺失类型转换:分割得到的字符串内容没有转成对应数值类型,直接调用strcpy向单字符变量拷贝内容会触发内存越界
  • 函数未前置声明:printAlunos定义在main函数之后,调用前无声明会触发编译错误
  • 未处理行尾换行:fgets读取的内容会携带行尾换行符,最后一个字段会被污染
  • 存在缓冲区溢出风险:文件名、姓名字段的读取没有预留字符串结束符的存储空间
完整可运行代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct aluno
{
    char nome[10];
    float p1;
    float p2;
} dict;

// 函数前置声明
void printAlunos(dict aluno[], int count);

int main()
{
    FILE *fp;
    dict aluno[9];
    char nomeArq[10];
    char buffer[1024];
    int i = 0;

    printf("Enter the file name: ");
    // 留1字节空间存字符串结束符,避免溢出
    scanf("%9[^\n]", nomeArq);
    // 清空输入缓冲区残留的换行符,避免后续fgets读空
    while(getchar() != '\n');

    if ((fp = fopen(nomeArq, "r")) == NULL)
    {
        printf("Error opening file: %s\n", nomeArq);
        return 1;
    }

    // 读取并跳过第一行表头
    if (fgets(buffer, sizeof(buffer), fp) == NULL)
    {
        printf("File is empty\n");
        fclose(fp);
        return 1;
    }

    // 逐行读取学生数据
    while (i < 9 && fgets(buffer, sizeof(buffer), fp) != NULL)
    {
        int campoCount = 0;
        char *token = strtok(buffer, ";");
        while (token != NULL)
        {
            // 清理token末尾的换行符
            char *newlinePos = strchr(token, '\n');
            if (newlinePos != NULL)
            {
                *newlinePos = '\0';
            }

            switch(campoCount)
            {
                case 0:
                    // 字符串拷贝留结束符位置,避免溢出
                    strncpy(aluno[i].nome, token, sizeof(aluno[i].nome) - 1);
                    aluno[i].nome[sizeof(aluno[i].nome) - 1] = '\0';
                    break;
                case 1:
                    // 字符串转浮点数存储
                    aluno[i].p1 = atof(token);
                    break;
                case 2:
                    aluno[i].p2 = atof(token);
                    break;
            }

            token = strtok(NULL, ";");
            campoCount++;
        }
        i++;
    }

    fclose(fp);
    // 传入实际读取到的学生数量
    printAlunos(aluno, i);

    return 0;
}

void printAlunos(dict aluno[], int count)
{
    for (int i = 0; i < count; i++)
    {
        printf("%s\n", aluno[i].nome);
        printf("%.1f %.1f\n", aluno[i].p1, aluno[i].p2);
    }
}
关键实现说明
  • 表头跳过逻辑:文件打开后优先读取第一行内容直接丢弃,不进入结构体存储流程
  • 逐行重置计数器:每读取新的一行数据时,将字段计数器重置为0,保证每行的三个字段都能对应到结构体的正确成员
  • 安全拷贝:姓名字段使用strncpy拷贝,强制在末尾加字符串结束符,避免缓冲区溢出
  • 换行清理:每个分割出的字段都会检查并移除末尾的换行符,避免输出时出现多余空行
  • 动态计数:不硬编码学生数量,实际读取到多少条就输出多少条,兼容不同行数的CSV文件
  • 类型匹配:分数字段改为float类型,通过atof将分割得到的字符串转为数值存储,输出时用浮点数格式化符打印

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 08:01:04