提问人:peemai2004 提问时间:11/8/2023 最后编辑:egleasepeemai2004 更新时间:11/8/2023 访问量:89
我的 C 程序从 .csv 计算每个学期的成绩的问题
Problem with my C program that calculate grades in each term from .csv
问:
我的 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 条件,当它是新主题时,但它不起作用
答:
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)。
我发现的问题是您没有检查 的返回值。几乎所有函数都会返回某种与成功程度相关的有用信息。 也不例外。从:sscanf
libc
sscanf
man 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;
}
评论
if (grades[numGrades - 1].numSubjects < MAX_SUBJECTS) {
else