上一节说道师弟的学生成绩管理系统缺少数据持久化,关于数据持久化的方法基本都是落盘,将内存信息回写磁盘。回写磁盘的方式有很多种,典型的就是普通的文件操作,还有就是数据库操作。这里将重点使用普通的文件操作,来实现数据回写磁盘,以及从磁盘读取数据。
一般文件操作步骤为: 打开文件->读写文件->关闭文件。
打开文件函数为: fopen
FILE *fopen(const char *filename, const char *mode);
关闭文件函数为: fclose
int fclose(FILE *stream);
读写文件:
//从文件读取数据块
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);//向文件写入数据块
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); //从文件中读取格式化输入
int fscanf(FILE *stream, const char *format, ...);//向文件写入格式化的字符串
int fprintf(FILE *stream, const char *format, ...);//从文件一次读取一行
char *fgets(char *str, int n, FILE *stream);//将字符串写入文件
int fputs(const char *str, FILE *stream);//从文件中读取下一个字符
int fgetc(FILE *stream);//写入一个字符到文件中
int fputc(int c, FILE *stream);
学生成绩管理系统写文件
int write_student_info(Student *s)
{FILE *fp = fopen(STUDENT_SYSTEM, "a");if (fp == NULL) {printf("fopen student_system failed!\n");return 1;}fprintf(fp, "%-4d %-10s %-.2f\n", s->id, s->name, s->score);fclose(fp);return 0;
}int check_if_student_exsit(int id)
{int i;for(i = 0; i < student_count; i++) {if(students[i].id == id) {return 1;}}return 0;
}void add_student()
{Student s;if(student_count < MAX_STUDENTS) {printf("Enter student ID: ");scanf("%d", &s.id);printf("Enter student name: ");scanf("%s", s.name);s.score = 0.0; // 初始成绩设置为0if (!check_if_student_exsit(s.id)) {students[student_count++] = s;printf("Student added successfully, all student: %d!\n", student_count);write_student_info(&s);} else {printf("student has in db, do nothing!\n");}} else {printf("Database is full!\n");}
}
读取文件进行系统初始化
仅仅支持写文件是不够的,还需要支持读取,这样才能保证每次程序运行的时候,数据初始化是正确的。
int init_student_info()
{FILE *fp = fopen(STUDENT_SYSTEM, "r");if (fp == NULL) {printf("fopen student_system failed!\n");return 1;}#define BUF_SIZE 1024char buf[BUF_SIZE];int i = 0;while(fgets(buf, BUF_SIZE - 1, fp) != NULL) {sscanf(buf, "%d %s %f\n", &students[i].id, students[i].name, &students[i].score);i++;student_count++;}fclose(fp);return 0;
}
故完整程序如下
#include <stdio.h>
#include <stdlib.h>
#include <string.h>#define MAX_STUDENTS 100
#define MAX_NAME_LEN 50
#define STUDENT_SYSTEM "student_system"typedef struct {int id; // 学号char name[MAX_NAME_LEN]; // 姓名float score; // 成绩
} Student;Student students[MAX_STUDENTS]; // 学生数组
int student_count = 0; // 学生数量int write_student_info(Student *s)
{FILE *fp = fopen(STUDENT_SYSTEM, "a");if (fp == NULL) {printf("fopen student_system failed!\n");return 1;}fprintf(fp, "%-4d %-10s %-.2f\n", s->id, s->name, s->score);fclose(fp);return 0;
}int check_if_student_exsit(int id)
{int i;for(i = 0; i < student_count; i++) {if(students[i].id == id) {return 1;}}return 0;
}void add_student()
{Student s;if(student_count < MAX_STUDENTS) {printf("Enter student ID: ");scanf("%d", &s.id);printf("Enter student name: ");scanf("%s", s.name);s.score = 0.0; // 初始成绩设置为0if (!check_if_student_exsit(s.id)) {students[student_count++] = s;printf("Student added successfully, all student: %d!\n", student_count);write_student_info(&s);} else {printf("student has in db, do nothing!\n");}} else {printf("Database is full!\n");}
}void print_title()
{printf("%-4s %-10s %-5s\n", "ID", "Name", "Score");
}void display_all_students()
{int i;printf("-------- All students info --------\n");if (student_count == 0) {printf("No students!\n");} else {print_title();for(i = 0; i < student_count; i++) {printf("%-4d %-10s %-.2f\n", students[i].id, students[i].name, students[i].score);}}printf("-------- End -----------\n");
}void find_student_by_id()
{int id, i;printf("Enter student ID to search: ");scanf("%d", &id);for(i = 0; i < student_count; i++) {if(students[i].id == id) {print_title();printf("%-4d %-10s %-.2f\n", students[i].id, students[i].name, students[i].score);return;}}printf("Student with ID %d not found!\n", id);
}void find_student_by_name()
{int i, is_find = 0;char name[MAX_NAME_LEN];printf("Enter student name to search: ");scanf("%s", name);for(i = 0; i < student_count; i++) {if(strcmp(students[i].name, name) == 0) {print_title();printf("%-4d %-10s %-.2f\n", students[i].id, students[i].name, students[i].score);is_find = 1;}}if (is_find == 0) {printf("Student with name %s not found!\n", name);}
}void add_score()
{int id, i;float score;printf("Enter student ID: ");scanf("%d", &id);printf("Enter student score: ");scanf("%f", &score);for(i = 0; i < student_count; i++) {if(students[i].id == id) {students[i].score = score;printf("Score added successfully!\n");return;}}printf("Student with ID %d not found!\n", id);
}void display_average_score()
{float total = 0.0;int i;for(i = 0; i < student_count; i++) {total += students[i].score;}printf("Average score of all students: %.2f\n", total / student_count);
}int init_student_info()
{FILE *fp = fopen(STUDENT_SYSTEM, "r");if (fp == NULL) {printf("fopen student_system failed!\n");return 1;}#define BUF_SIZE 1024char buf[BUF_SIZE];int i = 0;while(fgets(buf, BUF_SIZE - 1, fp) != NULL) {sscanf(buf, "%d %s %f\n", &students[i].id, students[i].name, &students[i].score);i++;student_count++;}fclose(fp);return 0;}int main()
{int choice;int ret;ret = init_student_info();if (ret) {printf("init_student_info failed!\n");return 1;}display_all_students();do {printf("\nStudent Score Management System\n");printf("1. Add Student\n");printf("2. Display All Students\n");printf("3. Find Student by ID\n");printf("4. Find Student by Name\n");printf("5. Add Score\n");printf("6. Display Average Score\n");printf("7. Exit\n");printf("Enter your choice: ");scanf("%d", &choice);switch(choice) {case 1:add_student();break;case 2:display_all_students();break;case 3:find_student_by_id();break;case 4:find_student_by_name();break;case 5:add_score();break;case 6:display_average_score();break;case 7:printf("Exiting...\n");break;default:printf("Invalid choice!\n");}} while(choice != 7);return 0;
}
不过仅仅这些还是不够的,比如如果有学生退学了,在显示所有学生成绩的时候,就不应该再显示这些学生。
这里我们可以在定义学生结构体的时候,增加一个数据有效位标识。定义如下:
typedef struct {int id; // 学号char name[MAX_NAME_LEN]; // 姓名float score; // 成绩int valid; //数据是否有效
} Student;
这样是否就够了呢?其实我们还是可以有很多优化的地方,比如: 当前最大支持学生数为100,如果超过100以后,该怎么处理呢?
任何程序和系统应该都有优化的地方,还是值得我们去深入研究和思考。