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

C语言读取CSV文件到结构体时格式错误问题求助

问题:C语言读取CSV文件存入结构体失败

问题描述

在使用C语言读取CSV文件并存储到结构体时遇到以下问题:

  • CSV文件每行格式为「姓名(空格分隔名+姓)+3个成绩」,但程序始终输出File format incorrect.,调整判断值(2、3、5)也无法解决。
  • 将判断条件改为read == 1时,程序输出8条混乱的记录。

测试用CSV文件内容

Iskandar Kholmatov,100,100,100
George Washington,90,50,100
Dennis Ritchie,90,0,10
Bill Gates,60,50,77

原错误代码

#include <stdio.h>

// struct to hold the name of a student
struct name
{
  char first[20]; // string to hold the first name
  char last[20]; // string to hold the last name
};

// struct to hold the grades of a student
struct student
{
  struct name Name; // name struct from above
  float grades[3]; // array to hold 3 grades
  float average; // float to hold the average of 3 grades above
};

int main(void)
{
  // file pointer variable for accessing the file
  FILE *file;

  // attempt to open file.txt in read mode to read the file contents
  file = fopen("data.csv", "r");

  // if the file failed to open, exit with an error message and status
  if (file == NULL)
  {
    printf("Error opening file.\n");
    return 1;
  }

  struct student students[5];

  int read = 0;

  // records will keep track of the number of Student records read from the file
  int records = 0;

  // read all records from the file and store them into the students array
  do
  {
    read = fscanf(file, "%s,%s,%f,%f,%f\n",
           students[records].Name.first,
           students[records].Name.last,
           &students[records].grades[0],
           &students[records].grades[1],
           &students[records].grades[2]);


    // if fscanf read 4 values from the file then we've successfully read
    // in another record
    if (read == 4)
      records++;

    // The only time that fscanf should NOT read 4 values from the file is
    // when we've reached the end of the file, so if fscanf did not read in
    // exactly 4 values and we're not at the end of the file, there has been
    // an error (likely due to an incorrect file format) and so we exit with
    // an error message and status.
    if (read != 4 && !feof(file))
    {
      printf("File format incorrect.\n");
      return 1;
    }

    // if there was an error reading from the file exit with an error message
    // and status
    if (ferror(file))
    {
      printf("Error reading file.\n");
      return 1;
    }

  } while (!feof(file));

  // close the file as we are done working with it
  fclose(file);

  // print out the number of records read
  printf("\n%d records read.\n\n", records);

  // print out each of the records that was read
  for (int i = 0; i < records; i++)
    printf("%s %s %f %f %f\n",
           students[i].Name.first,
           students[i].Name.last,
           students[i].grades[0],
           students[i].grades[1],
           students[i].grades[2]);
  printf("\n");

  return 0;
}

问题分析与修复步骤

1. 核心错误:格式字符串匹配逻辑错误

原代码的fscanf格式串"%s,%s,%f,%f,%f\n"存在两个致命问题:

  • %s以空白符(空格、换行)作为结束标志,无法正确读取CSV中包含空格的姓名字段。例如第一行Iskandar Kholmatov,100,100,100,第一个%s读取Iskandar,第二个%s会跳过空格读取Kholmatov,100,100,100(逗号不是空白符),导致后续%f无法匹配有效浮点数,fscanf返回值远小于预期。
  • 格式串包含5个占位符(名、姓、3个成绩),成功读取时fscanf应返回5,但原代码判断read ==4,逻辑完全错误。

2. 修正格式字符串

使用%[^,]格式说明符(匹配所有非逗号字符),结合空格匹配姓名分隔符,正确读取字段:

read = fscanf(file, "%[^ ] %[^,],%f,%f,%f\n",
              students[records].Name.first,
              students[records].Name.last,
              &students[records].grades[0],
              &students[records].grades[1],
              &students[records].grades[2]);
  • %[^ ]:匹配所有非空格字符,读取名字(如Iskandar)
  • 空格:匹配姓名中的空格分隔符
  • %[^,]:匹配所有非逗号字符,读取姓氏(如Kholmatov)
  • ,:匹配CSV中的逗号分隔符

3. 修正返回值判断逻辑

成功读取一条记录时fscanf返回5,因此调整判断条件:

if (read == 5)
  records++;

if (read != 5 && read != EOF)
{
  printf("File format incorrect.\n");
  return 1;
}

4. 优化读取循环(避免多读)

do-while(!feof(file))会导致文件末尾多读一次,改为先读取再判断的结构:

while (1)
{
  read = fscanf(file, "%[^ ] %[^,],%f,%f,%f\n",
                students[records].Name.first,
                students[records].Name.last,
                &students[records].grades[0],
                &students[records].grades[1],
                &students[records].grades[2]);

  if (read == EOF) break; // 读取到文件末尾,退出循环

  if (read != 5)
  {
    printf("File format incorrect.\n");
    return 1;
  }

  records++;
  if (records >= 5) break; // 防止数组越界
}

修复后的完整代码

#include <stdio.h>

struct name
{
  char first[20];
  char last[20];
};

struct student
{
  struct name Name;
  float grades[3];
  float average;
};

int main(void)
{
  FILE *file = fopen("data.csv", "r");
  if (file == NULL)
  {
    printf("Error opening file.\n");
    return 1;
  }

  struct student students[5];
  int read = 0;
  int records = 0;

  while (1)
  {
    read = fscanf(file, "%[^ ] %[^,],%f,%f,%f\n",
                  students[records].Name.first,
                  students[records].Name.last,
                  &students[records].grades[0],
                  &students[records].grades[1],
                  &students[records].grades[2]);

    if (read == EOF) break;

    if (read == 5)
      records++;
    else
    {
      printf("File format incorrect.\n");
      return 1;
    }

    if (ferror(file))
    {
      printf("Error reading file.\n");
      return 1;
    }

    if (records >= 5) break;
  }

  fclose(file);

  printf("\n%d records read.\n\n", records);
  for (int i = 0; i < records; i++)
    printf("%s %s %.2f %.2f %.2f\n",
           students[i].Name.first,
           students[i].Name.last,
           students[i].grades[0],
           students[i].grades[1],
           students[i].grades[2]);
  printf("\n");

  return 0;
}

预期输出

4 records read.

Iskandar Kholmatov 100.00 100.00 100.00
George Washington 90.00 50.00 100.00
Dennis Ritchie 90.00 0.00 10.00
Bill Gates 60.00 50.00 77.00

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 13:50:24