我的 C 程序从 .csv 计算每个学期的成绩的问题

Problem with my C program that calculate grades in each term from .csv

提问人:peemai2004 提问时间:11/8/2023 最后编辑:egleasepeemai2004 更新时间:11/8/2023 访问量:89

问:

我的 C 代码

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

#define MAX_GRADES 100
#define MAX_SUBJECTS 100
// struct to store subject data
typedef struct
{
  char code[50];
  char subjectName[50];
  float cr;
  float grade;
} Subject;
// struct to store grade data
typedef struct
{
  char grade[50];
  Subject subjects[MAX_SUBJECTS];
  int numSubjects;
  float avgGrade;
} GradeData;
// struct to store subject group data
typedef struct
{
  char subjectGroup[3];
  float totalGrade;
  int count;
} SubjectGroup;

int main()
{
  // change file name here
  char filename[] = "grades.csv";
  FILE *file, *outputFile;
  char line[100];
  GradeData grades[MAX_GRADES];
  int numGrades = 0;
  float totalGradeSum = 0;
  int totalNumSubjects = 0;
  SubjectGroup subjectGroups[MAX_SUBJECTS];
  int numSubjectGroups = 0;
  int isFirstLine = 1;

  file = fopen(filename, "r");
  // in case file does not exist
  if (file == NULL)
  {
    perror("Error opening file");
    return -1;
  }
  // create output file
  outputFile = fopen("output.csv", "w");
  // in case file does not exist
  if (outputFile == NULL)
  {
    perror("Error creating output file");
    return -1;
  }
  // read file line by line
  while (fgets(line, sizeof(line), file)) {
        if (isFirstLine) {
            isFirstLine = 0;
            continue;
        }

        if (line[0] != ',') {
            if (line[0] != '\n') {
                sscanf(line, "%[^,],", grades[numGrades].grade);
                grades[numGrades].numSubjects = 0;
                numGrades++;
            }
        } else {
            Subject newSubject;
            sscanf(line, ",%[^,],%[^,],%f,%f", newSubject.code, newSubject.subjectName, &newSubject.cr, &newSubject.grade);
            if (grades[numGrades - 1].numSubjects < MAX_SUBJECTS) {
                strcpy(grades[numGrades - 1].subjects[grades[numGrades - 1].numSubjects].code, newSubject.code);
                strcpy(grades[numGrades - 1].subjects[grades[numGrades - 1].numSubjects].subjectName, newSubject.subjectName);
                grades[numGrades - 1].subjects[grades[numGrades - 1].numSubjects].cr = newSubject.cr;
                grades[numGrades - 1].subjects[grades[numGrades - 1].numSubjects].grade = newSubject.grade;
                grades[numGrades - 1].numSubjects++;
                totalGradeSum += newSubject.grade;
                totalNumSubjects++;

      // Calculate subject group averages
      char subjectGroup[3];
      strncpy(subjectGroup, newSubject.code, 2); // get first 2 characters of subject code
      subjectGroup[2] = '\0';                    // add null terminator
      int found = 0;
      for (int i = 0; i < numSubjectGroups; i++) // check if subject group already exists
      {
        if (strcmp(subjectGroup, subjectGroups[i].subjectGroup) == 0) // if subject group exists
        {
          // add grade to total grade and increment count
          subjectGroups[i].totalGrade += newSubject.grade; // add grade to total grade
          subjectGroups[i].count++;                        // increment count
          found = 1;                                       // set found to true
          break;
        }
      }
      if (!found) // if subject group does not exist
      {
        strcpy(subjectGroups[numSubjectGroups].subjectGroup, subjectGroup); // copy subject group to subject group data
        subjectGroups[numSubjectGroups].totalGrade = newSubject.grade;      // set total grade to grade
        subjectGroups[numSubjectGroups].count = 1;                          // set count to 1
        numSubjectGroups++;                                                 // increment number of subject groups
      }
    }
  }
  // calculate average grade for each grade
  for (int i = 0; i < numGrades; i++) 
  {
    // calculate average grade
    float sum = 0;
    for (int j = 0; j < grades[i].numSubjects; j++)
    {
      sum += grades[i].subjects[j].grade; // add grade to sum
    }
    grades[i].avgGrade = sum / grades[i].numSubjects; // calculate average grade
  }

  // write to output file
  // write grade data
  fprintf(outputFile, "Grade,Average Grade\n"); // write header
  for (int i = 0; i < numGrades; i++) // write grade data
  {
    fprintf(outputFile, "%s,%.2f\n", grades[i].grade, grades[i].avgGrade);
  }
  // write overall average grade
  fprintf(outputFile, "Overall Average Grade,%.2f\n",
          totalGradeSum / totalNumSubjects);
  // write subject group averages
  fprintf(outputFile, "\nSubject Group,Average Grade\n");
  for (int i = 0; i < numSubjectGroups; i++) // write subject group data
  {
    fprintf(outputFile, "%s,%.2f\n", subjectGroups[i].subjectGroup,
            subjectGroups[i].totalGrade / subjectGroups[i].count);
  }
  
  fclose(file); // close input file
  fclose(outputFile); // close output file

  return 0;
}

代码读取等级.csv

,CODE,Subject NAME,CR,Grade
Grade 10 Term 1,TH31101,Thai Language 1,1.0,1.5
,MA31101,Mathematics 1,1.0,4
,SC30101,Science (Physics),1.0,4
,EN31201,English Reading-Writing 1,1.5,4
,,,,
,,,,
Grade 10 Term 2,TH31102,Thai Language 2,1.0,4
,MA31102,Mathematics 2,1.0,4
,SC30103,Computational Science 1,0.5,3.5
,EN31202,English Reading - Writing 2,1.5,4

但它的输出在每个学期中都缺少第一个主题

Grade,Average Grade
Grade 10 Term 1,3.72
Grade 10 Term 2,3.88
Overall Average Grade,3.86

Subject Group,Average Grade
MA,3.75
SC,3.83
EN,4.00

我认为存在一个问题,它没有在每个术语中读取第一个主题,因为我与术语名称在同一行。

我曾尝试将计算数据的行克隆为 if 条件,当它是新主题时,但它不起作用

c csv struct 文件访问

评论

1赞 pmg 11/8/2023
建议:要么使用制表符缩进代码,要么只用制表符缩进代码......或空格,并且只有空格。不要将缩进与制表符和空格混合使用!!
1赞 Jason 11/8/2023
不确定这是否是复制粘贴错误,但看起来您缺少 的右大括号。你肯定在这里的某个地方错过了一个闭幕支架。编辑:仅根据缩进,看起来您也缺少一个大括号,但实际上您只缺少一个。if (grades[numGrades - 1].numSubjects < MAX_SUBJECTS) {else
0赞 Jason 11/8/2023
你期望输出是什么?
0赞 Chris 11/8/2023
如果将其分解为具有有意义的名称的函数,那就太好了。

答:

0赞 hko 11/8/2023 #1

如果您的 csv 文件始终以这种方式格式化,则只需读取每一行的主题,包括术语行。

if (line[0] != ',') {
    if (line[0] != '\n') {
        sscanf(line, "%[^,],%[^,],%[^,],%f,%f", grades[numGrades].grade, newSubject.code, newSubject.subjectName, &newSubject.cr, &newSubject.grade);
        grades[numGrades].numSubjects = 0;
        numGrades++;
    }
} else {
    sscanf(line, ",%[^,],%[^,],%f,%f", newSubject.code, newSubject.subjectName, &newSubject.cr, &newSubject.grade);
}
if (grades[numGrades - 1].numSubjects < MAX_SUBJECTS) {
    strcpy(grades[numGrades - 1].subjects[grades[numGrades - 1].numSubjects].code, newSubject.code);
    strcpy(grades[numGrades - 1].subjects[grades[numGrades - 1].numSubjects].subjectName, newSubject.subjectName);
    grades[numGrades - 1].subjects[grades[numGrades - 1].numSubjects].cr = newSubject.cr;
    grades[numGrades - 1].subjects[grades[numGrades - 1].numSubjects].grade = newSubject.grade;
    grades[numGrades - 1].numSubjects++;
    totalGradeSum += newSubject.grade;
    totalNumSubjects++;
}

这将为您提供输出:

Grade,Average Grade
Grade 10 Term 1,3.58
Grade 10 Term 2,3.88
Overall Average Grade,3.70

Subject Group,Average Grade
TH,2.75
MA,4.00
SC,3.75
EN,4.00
0赞 Jason 11/8/2023 #2

我不完全确定您在寻找什么输出。在我看来,平均值是错误的(例如,科学平均值应该是 3.75 而不是 3.83)。

我发现的问题是您没有检查 的返回值。几乎所有函数都会返回某种与成功程度相关的有用信息。 也不例外。从:sscanflibcsscanfman 3 sscanf

成功后,这些函数返回成功匹配和分配的输入项数;如果早期匹配失败,这可能小于规定值,甚至为零。

因此,您应该期望它将返回与您为其提供的格式说明符数相同的数字。例如,以下行:sscanf

sscanf(line, ",%[^,],%[^,],%f,%f", ...

...应返回 4。

当您在只有逗号的“空白”行上运行它时,它不会返回 4,同样也不会触及其中的信息。从本质上讲,这意味着对于每个仅逗号行,您都会处理前一个非逗号行。所以这里:

,EN31201,English Reading-Writing 1,1.5,4
,,,,
,,,,

...第一行被处理 3 次。

这里的解决方案是简单地检查输出,如果它不是我们预期的:continue

if (sscanf(line, ",%[^,],%[^,],%f,%f", newSubject.code, newSubject.subjectName, &newSubject.cr, &newSubject.grade) != 4) {
  continue;
}