c入门第十七篇——数据持久化,读写文件

上一节说道师弟的学生成绩管理系统缺少数据持久化,关于数据持久化的方法基本都是落盘,将内存信息回写磁盘。回写磁盘的方式有很多种,典型的就是普通的文件操作,还有就是数据库操作。这里将重点使用普通的文件操作,来实现数据回写磁盘,以及从磁盘读取数据。

一般文件操作步骤为: 打开文件->读写文件->关闭文件。

打开文件函数为: fopen

c 复制代码
FILE *fopen(const char *filename, const char *mode);

关闭文件函数为: fclose

c 复制代码
int fclose(FILE *stream);

读写文件:

c 复制代码
//从文件读取数据块
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);

学生成绩管理系统写文件

c 复制代码
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; // 初始成绩设置为0
        if (!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");
    }
}

读取文件进行系统初始化

仅仅支持写文件是不够的,还需要支持读取,这样才能保证每次程序运行的时候,数据初始化是正确的。

c 复制代码
int init_student_info()
{
    FILE *fp = fopen(STUDENT_SYSTEM, "r");
    if (fp == NULL) {
        printf("fopen student_system failed!\n");
        return 1;
    }

#define BUF_SIZE 1024
    char 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;
}

故完整程序如下

c 复制代码
#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; // 初始成绩设置为0
        if (!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 1024
    char 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;
}

不过仅仅这些还是不够的,比如如果有学生退学了,在显示所有学生成绩的时候,就不应该再显示这些学生。

这里我们可以在定义学生结构体的时候,增加一个数据有效位标识。定义如下:

c 复制代码
typedef struct {
    int id; // 学号
    char name[MAX_NAME_LEN]; // 姓名
    float score; // 成绩
    int valid; //数据是否有效
} Student;

这样是否就够了呢?其实我们还是可以有很多优化的地方,比如: 当前最大支持学生数为100,如果超过100以后,该怎么处理呢?

任何程序和系统应该都有优化的地方,还是值得我们去深入研究和思考。

相关推荐
mmsx33 分钟前
android sqlite 数据库简单封装示例(java)
android·java·数据库
bryant_meng36 分钟前
【python】OpenCV—Image Moments
开发语言·python·opencv·moments·图片矩
若亦_Royi1 小时前
C++ 的大括号的用法合集
开发语言·c++
zpjing~.~2 小时前
Mongo 分页判断是否有下一页
数据库
2401_857600952 小时前
技术与教育的融合:构建现代成绩管理系统
数据库·oracle
资源补给站2 小时前
大恒相机开发(2)—Python软触发调用采集图像
开发语言·python·数码相机
2301_819287122 小时前
ce第六次作业
linux·运维·服务器·网络
秋恬意2 小时前
Mybatis能执行一对一、一对多的关联查询吗?都有哪些实现方式,以及它们之间的区别
java·数据库·mybatis
CIb0la2 小时前
GitLab 停止为中国区用户提供 GitLab.com 账号服务
运维·网络·程序人生
m0_748247552 小时前
Web 应用项目开发全流程解析与实战经验分享
开发语言·前端·php