关于学生信息管理系统的优化

关于学生信息管理系统的优化

优化1:更改引导用户创建数据库的方案

    FILE *fp = fopen(file_name, "r");
    char s[100];
    int n;
    if (fp == NULL) {
        printf("未检测到数据库,请问是否创建(y / n)?");
        scanf("%s", s);
        printf("\n");
        if(s[0] == 'y') {
            printf("Please enter how many data items there are in total?(请输入共有多少项数据?) : ");
            scanf("%d", &n);
            printf("\n");
            FILE *p = fopen(file_name, "w");
            fclose(p);
            for (int i = 0; i < n; i++) {
                create_file();
            }
        }
    }
    else {
        stu_cnt = read_from_file(stu_arr);
    }
    fclose(fp);

利用读的特性,来检查数据库是否存在,如果不存在则引导用户创建数据库。

优化2:实现数据的单条修改操作

之前的方法是利用w打开文件使文件清空,然后写入更新后的数据。
优化后实现数据的单条修改。

typedef struct Student {
    long offset; //记录数据在文件中的位置
    char name[20];
    int age;
    int class;
    float height;
} Student;

更改结构体,增加记录数据在文件中位置的变量offset。

int read_from_file(Student *arr) {
    int i = 0;
    FILE *fp = fopen(file_name, "r");
    if (fp == NULL) return i;
    while (1) {
        long offset = ftell(fp);
        if (fscanf(fp, "%s", arr[i].name) == EOF) break;
        fscanf(fp, "%d%d%f", 
        	&arr[i].age,
        	&arr[i].class,
        	&arr[i].height
        );
        arr[i].offset = offset;
        fgetc(fp); //吞掉换行符
        i += 1;
    }
    fclose(fp);
    return i;
}

在从文件中读数据的代码段中维护offset的值。

注意细节:当读取完一行数据后,文件指针的位置位于一行数据末尾的换行符那里。需要用fgetc()来吞掉换行符。

void student_add() {
    printf("add new item : (name, age, class, height)\n");
    printf("mysql > ");
    scanf("%s%d%d%f",
        stu_arr[stu_cnt].name, 
        &stu_arr[stu_cnt].age, 
        &stu_arr[stu_cnt].class, 
        &stu_arr[stu_cnt].height
    );
    output_arr_file(stu_arr + stu_cnt, 1);
    stu_cnt += 1;
    int n = printf("add a astudent success!\n");
    printf("\n");
    for (int i = 0; i < n; i++) {
        printf("-");
    }
    printf("\n\n");
    return ;
}

那么如何维护在添加学生时数据的偏移量信息呢 ?

long output_arr_file(Student *arr, int n) {
    FILE *fp = fopen(file_name, "a");
    fseek(fp, 0, SEEK_END); //设置指针在文件的末尾
    long offset = ftell(fp);
    for (int i = 0; i < n; i++) {
    	fprintf(fp, "%s %d %d %.2f\n", 
    		arr[i].name, arr[i].age,
    		arr[i].class, arr[i].height
    	); 
    fclose(fp);
    return offset;
}

通过修改output_arr_file函数的返回值来维护偏移量信息。

void student_delete() {
    if (stu_cnt == 0) {
        printf("There is no student.\n");
        return ;
    }
    int id;
    student_list();
    do {
       printf("delete id : ");
       scanf("%d", &id);
    } while (id < 0 || id >= stu_cnt);
    printf("confirm (y / n) : ");
    fflush(stdin);
    char s[100];
    scanf("%s", s);
    if(s[0] != 'y') return ;
    for (int i = id + 1; i < stu_cnt; i++) {
        stu_arr[i - 1] = stu_arr[i];
    }
    stu_cnt -= 1;
    restore_data_to_file(stu_arr, stu_cnt);
    return ;
}
    for (int i = id + 1; i < stu_cnt; i++) {
        stu_arr[i - 1] = stu_arr[i];
    }

在删除学生信息的这段代码将会导致偏移量信息的错误。

    for (int i = id + 1; i < stu_cnt; i++) {
        long offset = stu_arr[i - 1].offset;
        stu_arr[i - 1] = stu_arr[i];
        stu_arr[i - 1].offset = offset;
    }

改为不更新数据偏移量。

void student_modify() {
    int id;
    student_list();
    do {
       printf("modify id : ");
       scanf("%d", &id);
    } while (id < 0 || id >= stu_cnt);
    printf("modify the item : (name, age, class, height)\n");
    scanf("%s%d%d%f",
        stu_arr[id].name, 
        &stu_arr[id].age, 
        &stu_arr[id].class, 
        &stu_arr[id].height
    );
    restore_data_to_file(stu_arr, stu_cnt);
    return ;
}

当前代码为清空文件并更新数据,但不会更新偏移量。
实现一个方法来维护数据的偏移量。

void modify_data_to_file(Student *stu) {
    FILE *fp = fopen(file_name, "r+");
    fseek(fp, stu->offset, SEEK_SET);
    fprintf(fp, "%s %d %d %.2f\n",
    	stu->name, 
    	stu->age, 
    	stu->class, 
    	stu->height
    );
    fclose(fp);
    return ;
}

当然这种方法有一个bug:如果文件中的学生数据是固定长度的,那么直接覆盖是可行的。如果记录是可变长度的,这种方法可能会导致文件内容出错。

#define output_format = "%10s%4d%4d%7.2f"

实现等宽输入。

void modify_data_to_file(Student *stu) {
    FILE *fp = fopen(file_name, "r+");
    fseek(fp, stu->offset, SEEK_SET);
    fprintf(fp, output_format,
    	stu->name, 
    	stu->age, 
    	stu->class, 
    	stu->height
    );
    fclose(fp);
    return ;
}
long output_arr_file(Student *arr, int n) {
    FILE *fp = fopen(file_name, "a");
    fseek(fp, 0, SEEK_END);
    long offset = ftell(fp);
    for (int i = 0; i < n; i++) {
    	fprintf(fp, output_format "\n", 
    		arr[i].name, arr[i].age,
    		arr[i].class, arr[i].height
    	); 
    fclose(fp);
    return offset;
}

优化3:实现数据的二进制存储

为什么要用二进制存储?

  1. 存储效率高
    二进制存储直接以机器码的形式存储数据,而不是将数据转换为可读的文本形式。这意味着同样的数据占用的存储空间更少。例如,整数在二进制形式下只需要4个字节,而在文本形式下可能需要更多的字节来表示。
  1. 读写速度快
    由于二进制存储不需要进行格式转换,读写速度通常比文本存储更快。特别是在处理大量数据时,这种速度优势更加明显。
  1. 数据精度高
    在二进制存储中,数据不会因为转换为文本而丢失精度。对于浮点数和其他精度敏感的数据类型尤其重要。
const char *file_name = "student_data.dat";

更改文件后缀名。

int read_from_file(Student *arr) {
    int i = 0;
    FILE *fp = fopen(file_name, "rb");
    if (fp == NULL) return i;
    while (1) {
        long offset = ftell(fp);
        if (fread(&arr[i], sizeof(Student), 1, fp) == 0) break;
        arr[i].offset = offset;
        i += 1;
    }
    fclose(fp);
    return i;
}

fread的返回值代表了成功读入几个字节。
二进制文件不必吞掉换行符。

long output_arr_file(Student *arr, int n) {
    FILE *fp = fopen(file_name, "ab");
    fseek(fp, 0, SEEK_END);
    long offset = ftell(fp);
    fwrite(arr, sizeof(Student), n ,fp);
    fclose(fp);
    return offset;
}
void modify_data_to_file(Student *stu) {
    FILE *fp = fopen(file_name, "rb+");
    fseek(fp, stu->offset, SEEK_SET);
    fwrite(stu, sizeof(Student), 1, fp);
    fclose(fp);
    return ;
}

文件打开方式改为操作二进制文件的,把fprintf改为fwrite,把fscanf改为fread。

小结

恭喜你!完成了学生信息管理系统的优化,向着C语言的殿堂更近了一步。

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值