自定义preconf.h
、kernel_list.h
、sysbrowse
三个头文件,完成了核心头文件前的预配置、核心头文件的操作、信息浏览系统的功能。
构建文件数据流读写的相关操作,便于保存学生信息表、系统的设置(存储路径、控制台颜色等)
GitHub:https://github.com/ITchujian/StudentManagementSystem_2022_C
注:为方便分享本次开发的经历,我把分析过程以及代码书写过程,以文字、图片形式合计放于开发记录中,但是一些非常基础的行为动作我将不会讲解或者阐述。
当前位置:【开发细节】用C语言基础写学生管理系统(五)
可跳转:
老规矩,创建一个名为sysdoc.h
的头文件,并包含一下自定义的头文件kernel_list.h
。
#include "kernel_list.h"
然后,我们将在当前运行的文件夹下创建一个名为SMSdir
的文件夹,用于存储学生信息表、系统设置。
创建一个名为createFolder
的函数,它的作用:创建多级目录。
Status createFolder(char* path)
{
char folder_path[128];
sprintf(folder_path, "mkdir %s", path);
if (_access(path, 0) == -1)
{
system(folder_path);
}
return OK;
}
面临的一大问题,如何存储我们的数据和结构体呢,那不妨用数据流二进制吧。
写入二进制流:
Status writeFile(char* path, void* struct_s, int s_size, int length)
{
FILE* fp;
fopen_s(&fp, path, "wb+");
if (fp != 0)
{
fwrite(struct_s, s_size, length, fp);
fclose(fp);
return OK;
}
else
return INFEASIBLE;
}
读取二进制流:
Status readFile(char* path, void* struct_s, int s_size, int length)
{
FILE* fp;
fopen_s(&fp, path, "rb+");
if (fp != 0)
{
fread(struct_s, s_size, length, fp);
fclose(fp);
return OK;
}
else
return INFEASIBLE;
}
我们需要哪些,学生们都在ElemType
结构体中,所以我们应该存储这个结构体,不妨将这个文件命名为:students.bin
。系统设置放在:config.bin
,它是最先加载的,它将加载students.bin
的存储路径、备份路径、以及控制台颜色值,那就有人纳闷了,我在preconf.h
中明明是这样定义的:
typedef struct
{
char file_path[128];
char backup_path[128];
char list_path[128];
char sys_color[32];
} SysConfig;
SysConfig config_bin = {};
backup_path
还好理解,备份路径,那list_path
是个什么鬼?
list_path
当然就是存储SqList
结构体的存储路径啦,解释一下为何要这个存储路径:
如果我们的项目是这样运行:加载config.bin→加载students.bin→控制台输出菜单,然后就是根据用户操作,假设我们做浏览所有学生信息的操作,该函数是如此定义的:
Status putAllList(SqList* L)
{
int i = 0;
printf("┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n");
printf("┃ 姓名 学号 性别 年龄 语文 数学 英语 平均分 总分 ┃\n");
printf("┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫\n");
while (i < L->length)
{
printf("┃ %-9s%-16d%-12s%-11d%-12.2f%-12.2f%-12.2f%-13.2f%-8.2f┃\n", (L->elem[i]).name, (L->elem[i]).num, (L->elem[i]).sex, (L->elem[i]).age, (L->elem[i]).score_literature, (L->elem[i]).score_math, (L->elem[i]).score_english, (L->elem[i]).average_score, (L->elem[i]).sum_score);
++i;
}
printf("┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n");
return OK;
}
也就是说,如果我们按照上述方式运行,我们的L->length
仅仅被InitList(SqList* L)
初始化了,它对应的值是0,而我们后续的行为操作、功能实现,都是要基于这个值的,无论你如何加载,尽管students.bin
文件存储了上万条学生的信息,依然打印不出一条的,千万记得,我们的核心是线性表SqList
,脱离了其中的变量是不可行的。所以,我们优化一下我们的运行逻辑:
加载config.bin→加载list_path.bin→加载students.bin→控制台输出菜单→用户操作
现在才是正确的逻辑,否则你永远操作的是0长度的线性表,毫无意义。
以上是我开发项目时忽略的问题,打上断点后,才发现的L->length一直是0
,真的是太大意了。
依次编写上述的加载方法。
Status loadConfig(void)
{
char dir_path[] = ".\\SMSdir";
createFolder(dir_path);
char config_path[128];
sprintf(config_path, "%s\\config.bin", dir_path);
readFile(config_path, &config_bin, sizeof(SysConfig), 4);
system(config_bin.sys_color);
return OK;
}
然后是加载list_path.bin
Status loadList(SqList* L)
{
readFile(config_bin.list_path, L, sizeof(SqList), 3);
return OK;
}
最后是加载students.bin
Status loadStu(SqList* L)
{
readFile(config_bin.file_path, L->elem, sizeof(ElemType), L->length);
return OK;
}
sysdoc.h
の完整代码:#pragma once
/*********************************************************************
* 转载请注明来源/Reprint please indicate the source
* @FileName sysdoc.h
* @Description 文件读写操作相关文件/File read and write operations related files
* @History
* version author data introduction and operations
* 1.0 初见 2022-01-23 Create
* *** *** ****-**-** *******
*/
#include "kernel_list.h"
/*********************************************************************
* @chujian(cn) 引入自定义头文件
* @chujian(en) Import custom header files
*/
Status setMenu(void)
{
printf("┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n");
printf("┃ 【副】系 统 设 置 ┃\n");
printf("┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫\n");
printf("┃ 警告 通常情况下,我并不建议您更改除3以外的设置 ┃\n");
printf("┃请选择操作: ┃\n");
printf("┃ 1 > 更改存储路径 ┃\n");
printf("┃ 2 > 更改备份路径 ┃\n");
printf("┃ 3 > 设置前背景颜色 ┃\n");
printf("┃ 4 > [危]内核列表路径 ┃\n");
printf("┃ 0 > 返回主系统 ┃\n");
printf("┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n");
printf("\nINPUT:");
return OK;
}
/*********************************************************************
* @chujian(cn) 系统设置界面
* @chujian(en) System setting interface
*/
Status colorMenu(void)
{
printf("颜色参考表:\n");
printf("0 = 黑色 8 = 灰色\n");
printf("1 = 蓝色 9 = 淡蓝色\n");
printf("2 = 绿色 A = 淡绿色\n");
printf("3 = 浅绿色 B = 淡浅绿色\n");
printf("4 = 红色 C = 淡红色\n");
printf("5 = 紫色 D = 淡紫色\n");
printf("6 = 黄色 E = 淡黄色\n");
printf("7 = 白色 F = 亮白色\n");
return OK;
}
/*********************************************************************
* @chujian(cn) 对应系统设置中的第三项
* @chujian(en) Corresponds to the third item in the system settings
*/
Status createFolder(char* path)
{
char folder_path[128];
sprintf(folder_path, "mkdir %s", path);
if (_access(path, 0) == -1)
{
system(folder_path);
}
return OK;
}
/*********************************************************************
* @chujian(cn) 创建目录
* @chujian(en) Create a directory
*/
Status writeFile(char* path, void* struct_s, int s_size, int length)
{
FILE* fp;
fopen_s(&fp, path, "wb+");
if (fp != 0)
{
fwrite(struct_s, s_size, length, fp);
fclose(fp);
return OK;
}
else
return INFEASIBLE;
}
/*********************************************************************
* @chujian(cn) 将数据流写入.bin文件
* @chujian(en) Write data stream to .bin file
*/
Status readFile(char* path, void* struct_s, int s_size, int length)
{
FILE* fp;
fopen_s(&fp, path, "rb+");
if (fp != 0)
{
fread(struct_s, s_size, length, fp);
fclose(fp);
return OK;
}
else
return INFEASIBLE;
}
/*********************************************************************
* @chujian(cn) 从.bin文件中读取数据流
* @chujian(en) Read data stream from .bin file
*/
Status loadConfig(void)
{
char dir_path[] = ".\\SMSdir";
createFolder(dir_path);
char config_path[128];
sprintf(config_path, "%s\\config.bin", dir_path);
readFile(config_path, &config_bin, sizeof(SysConfig), 4);
system(config_bin.sys_color);
return OK;
}
/*********************************************************************
* @chujian(cn) 初始化配置config.bin文件
* @chujian(en) Initialize the configuration config.bin file
*/
Status loadList(SqList* L)
{
readFile(config_bin.list_path, L, sizeof(SqList), 3);
return OK;
}
/*********************************************************************
* @chujian(cn) 加载内核线性表中除对应elem的数据
* @chujian(en) Load the data in the kernel linear table except the student structure
*/
Status loadStu(SqList* L)
{
readFile(config_bin.file_path, L->elem, sizeof(ElemType), L->length);
return OK;
}
/*********************************************************************
* @chujian(cn) 加载上一次保存的学生信息表
* @chujian(en) Load the last saved student information sheet
*/