提问人:Pepinho02 提问时间:3/3/2023 最后编辑:chqrliePepinho02 更新时间:3/3/2023 访问量:81
使用 fscanf() 和 fseek() 读取和编辑结构体
Reading and editing structs with fscanf() and fseek()
问:
我正在执行以下程序:
编程教师希望创建一个程序来计算学生的期末成绩。目前,所有学生的部分成绩都位于以下格式的文本文件中:
<first_name> <last_name1> <last_name2> | <AC1> <AC2> <AC3> <AC4> <AC5> <AC6> <AC7> <AC8> <AC9> <AC10> | <project 1> <project 2> <project 3> <report> | <exam>
例如:
Joan Gomez Garcia | 8.0 9.0 7.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0 | 10.0 9.0 8.5 9.0 | 6.0
Juan Perez Gonzalez | 3.0 3.0 4.8 5.0 5.0 4.0 6.0 2.0 0.0 0.0 | 8.0 7.5 7.0 8.0 | 7.5
Marta Bonet Call | 6.0 7.0 7.0 8.0 8.0 9.0 10.0 10.0 0.0 | 9.0 9.0 9.0 10.0 | 5.5
Jordi Fernandez Bou | 10.0 10.0 10.0 10.0 10.0 10.0 10.0 10.0 10.0 | 10.0 10.0 10.0 10.0 | 3.5
所有值都用空格分隔,连续评估分数、项目分数和期末考试分数之间有一个斜杠 ('|')。此信息必须存储在 Student 类型的结构中。您可以在下面所示的 grades.h 文件的 typedef 中找到它的定义。
连续评价 1 应存储在 CA[0] 盒中,连续评价 2 应存储在 CA[1] 中,依此类推,直到连续评价 10。练习 1 的成绩应保存在项目 [0] 中,练习 2 应保存在项目 [1] 中,练习 3 应保存在项目 [2] 中。内存注释必须保存在报告字段中。最后,考试成绩必须保存在final_exam中。
要计算每个学生的最终成绩,必须进行以下计算:
学期成绩 = 50% 知识成绩 + 50% 项目成绩
知识等级是以下两者之间的最高值:
知识等级 = 70% 期末考试 + 30% 10 个 CA 的平均分
或
知识等级 = 100% 期末考试
练习成绩的计算公式为:
项目等级 = 30% 项目 1 + 30% 项目 2 + 30% 项目 3 + 10% 报告
计算最终成绩的每个学生的记录必须保存在名为“students.bin”的二进制文件中。 应用模块概念,我们想创建一个模块来求解它,该模块由文件 grades.h 和 grades.c 组成。grades.h 文件如下所示:
#ifndef _GRADES_H
#define _GRADES_H
#include <stdio.h>
#define MAX 100
#define MAX_STR 50
typedef char String[31];
typedef struct {
String first_name;
String last_name1;
String last_name2;
float CA[10];
float Project[3];
float report;
float final_exam;
float sem_grade;
} Student;
int loadGrades(char filename[MAX_STR], Student students[MAX]);
void computeGrades(Student students[MAX], int n_students);
void save(Student students[MAX], int n_students);
void updateFile();
#endif
您的任务是编写 grades.c 文件的内容。loadGrades() 函数打开并读取 filename 的内容,并将所有信息存储在 students 数组中,并返回已从文件中读取的学生数。computeGrades() 过程在考虑 CA、项目和期末考试的情况下计算最后一个学期的成绩,并更新sem_grade字段。save() 过程将数组 students 的全部内容保存在 Student 类型的二进制文件 (“students.bin”) 中。
最后,评分系统表明,学期成绩达到 5.0 或更高但期末考试成绩未达到至少 4.0 分的学生将获得 4.0 分作为学期的最终成绩。有必要搜索整个学生二进制文件(“students.bin”)以查找这些情况,并更改发生这种情况的所有学期成绩。这就是 updateFile() 函数将要执行的操作。
主要有:
#include <stdio.h>
#include "grade.h"
int main()
{ char filename[MAX_STR];
Student students[MAX];
int n_students = 0;
printf("Enter filename: ");
scanf("%s", filename);
n_students = loadGrades(filename, students);
computeGrades(students, n_students);
save(students, n_students);
updateFile();
return 0;
}
这是可编辑的代码:
int loadGrades(char filename[], Student students[]) {
char buffer[MAX_STR];
int n_students = 0;
int i = 0;
int j = 0;
FILE *fitxer = NULL;
fitxer = fopen(filename, "r");
if (NULL == fitxer) {
printf("error");
} else {
fscanf(fitxer, "%s", students[i].first_name);
while (!feof(fitxer)) {
fscanf(fitxer, "%s", students[i].last_name1);
fscanf(fitxer, "%s", students[i].last_name2);
fscanf(fitxer, "%s", buffer);
for (j = 0; j < 10; j++) {
fscanf(fitxer, "%f", &students[i].CA[j]);
}
fscanf(fitxer, "%s", buffer);
for (j = 0; j < 3; j++) {
fscanf(fitxer, "%f", &students[i].Project[j]);
}
fscanf(fitxer, "%f", &students[i].report);
fscanf(fitxer, "%s", buffer);
fscanf(fitxer, "%f", &students[i].final_exam);
n_students++;
fscanf(fitxer, "%s", students[i].first_name);
}
fclose(fitxer);
}
return n_students;
}
void computeGrades(Student students[], int n_students) {
int i = 0;
int j = 0;
float average = 0;
float kg = 0;
float pg = 0;
for (i = 0; i < n_students; i++) {
average = 0;
kg = 0;
pg = 0;
for (j = 0; j < 10; j++) {
average += students[i].CA[j];
}
average /= 10;
pg = students[i].Project[0] * 0.3 + students[i].Project[1] * 0.3 +
students[i].Project[0] * 0.3 + students[i].report * 0.1;
kg = 0.7 * students[i].final_exam + 0.3 * average;
if (students[i].final_exam > kg) {
kg = students[i].final_exam;
}
students[i].sem_grade = 0.5 * kg + 0.5 * pg;
}
}
void save(Student students[], int n_students) {
FILE *fitxer = NULL;
int i = 0;
fitxer = fopen("students.bin", "wb");
if (NULL != fitxer) {
for (i = 0; i < n_students; i++) {
fwrite(&students[i], sizeof(Student), 1, fitxer);
}
}
fclose(fitxer);
}
void updateFile() {
Student student;
FILE *fitxer = NULL;
int i = 1;
char filename[MAX_STR];
strcpy(filename, "students.bin");
fitxer = fopen(filename, "rb+");
if (NULL != fitxer) {
fread(&student, sizeof(Student), 1, fitxer);
while (!feof(fitxer)) {
fseek(fitxer, i * sizeof(Student), SEEK_SET);
if ((5 <= student.sem_grade) && (4 > student.final_exam)){
student.sem_grade = 4;
}
fseek(fitxer, i * sizeof(Student), SEEK_SET);
fwrite(&student, i - 1 * sizeof(student), 1, fitxer);
i++;
fread(&student, sizeof(Student), 1, fitxer);
}
} else {
printf("error");
}
}
我认为我的问题在于函数,不知何故,我只将最后一个学生存储在文件中。loadGrades()
答:
我认为你的问题的解决方案是你永远不要在你的 while 循环中增加 i。您可能希望将 i 替换为 n_students 来解决这个问题并节省一些空间。我很确定你的代码中还有更多的错误,但我们不是在这里为你做功课;)
为您提供更多提示:
- 尽可能本地声明
- 传递指针而不是数组, 从技术上讲,两者是相同的,但某人获得的概率 错误地使用 sizeof() 的想法减少了
- 使用 if-else 的 Guard Conditions 即时进行错误处理
代码中存在多个问题:
永远不要用于测试文件末尾,只需测试 和 的返回值。
feof()
fscanf()
fread()
中的元素长度似乎无效。你的意思是:
fwrite(&student, i - 1 * sizeof(student), 1, fitxer);
fwrite(&student, sizeof(student), 1, fitxer);
您必须在更新模式下对文件进行读取和写入的调用之间发出调用。
fseek()
rewind()
更新模式非常混乱且容易出错,您应该考虑使用不同的方法。
评论
Student
fscanf(fitxer, "%s",....
很糟糕。它应该有一个宽度,以防止缓冲区溢出。应检查返回值。 无法读取姓氏中包含空格的姓氏。"%s"