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以后,该怎么处理呢?

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

相关推荐
Nicole Potter4 分钟前
请说明C#中的List是如何扩容的?
开发语言·面试·c#
liuyuzhongcc6 分钟前
List 接口中的 sort 和 forEach 方法
java·数据结构·python·list
jiugie23 分钟前
MongoDB学习
数据库·python·mongodb
hzulwy26 分钟前
MongoDB应用设计调优
数据库·mongodb
bugtraq202134 分钟前
XiaoMi Mi5(gemini) 刷入Ubuntu Touch 16.04——安卓手机刷入Linux
linux·运维·ubuntu
xmweisi40 分钟前
【华为】报文统计的技术NetStream
运维·服务器·网络·华为认证
十八朵郁金香42 分钟前
通俗易懂的DOM1级标准介绍
开发语言·前端·javascript
我爱松子鱼1 小时前
MySQL 单表访问方法详解
数据库·mysql
阿尔法波1 小时前
python与pycharm如何设置文件夹为源代码根目录
开发语言·python·pycharm
xing25161 小时前
pytest下allure
开发语言·python·pytest