C语言学生信息管理系统详细设计
学生信息管理系统
授权协议
: 允许对本文提供的任何信息、资料进行使用、学习、修改等,但请注明引用出处。(白tom: qq1005252070)
1. 问题定义
1.1. 概述
不论是在校计算机专业的学生还是刚步入IT行业从事C工作的毕业生,对于C语言的基础考核,学生管理系统则作为不二的选择。该项目不论从难易程度还是知识考核点都易于被接受。
1.2. 需求分析
基于以上学生管理系统动图需求分析应包含以下基本功能:
- 学生基本信息
- 学生信息查询、增加、删除、修改
- 学生信息持久化
- UI界面显示等
实现以上功能存在如下疑问:
- 如何实现光标移动 ?
- 如何实现清屏 ?
- 如何实现数据操作 ?
- 如何实现数据持久化 ?
具体设计考虑参照2. 设计考虑
1.3. 平台约束
- 软件环境
- VC++ 6.0
- windows
- 开发语言
- c 语言
2. 设计考虑
2.1. 功能设计考虑
2.1.1. 学生基本信息考虑
学生信息可暂定包含如下:
- 姓名
- 学号(唯一性)
- 年龄
- 绩点
- 学费
- del标志位
2.1.2. 功能操作考虑
对于增删改查存在以下两种方案:
- 直接操作文件
- 将文件读取到链表,对链表进行处理后再更新进文件
优劣分析:方案一 操作文件效率低下,在内存较少的情况下并且对于速度性能要求不高时候可以直接进行操作比如pos机等。对于方案二.直接操作链表,链表是存储在内存中的。所以性能高,但对于开发人能要求较高,可能存在内存泄露等。基于以上分析,本次需求采用链表方式,此方案巩固了C语言核心功能链表操作。
2.1.2. 屏幕光标考虑
正常在校生学习到的输入输出函数printf,scanf等无法满足界面光标定位,为了使得界面操作更加人性化,采用windows 提供控制台光标函数SetConsoleCursorPosition。该函数可任意定位到控制台(黑乎乎的窗口)坐标点。对应头文件为:#include <windows.h>
2.1.2. 屏幕清屏考虑
基础c语言程序编程只会在界面上直接打印出一连串输出,用户不断输入,界面不断输出,极其难看,为了满足人性化,当前清屏可采用以下两种方案:
方案一:采用system(“cls”)进行清屏。
方案二:采用光标定位位置+ printf(" ")空格进行清屏。
优劣分析如下: 采用方案一:界面会出现闪烁的效果,但不影响使用,方案二:需实时设置清除的位置,如果封装的好具有更强的灵活性。对于本次项目采用以上两种结合的方式,主界面直接cls清除,对于部分显示数据则采用方案二只清除部分区域。
2.1.3. 学生信息持久化考虑
持久化顾名思义就是数据永久保存,可以采用文件、或者数据库(sqlite3)保存。当前考核c语言基础,则直接采用文件方式进行管理。
2.1.4. 学生信息删除考虑
数据的删除分为真删
和假删
,真删
既直接将数据删除,保存进文件,缺点如果想要查找之前学生信息则无法查找到。假删
即在信息中增加标志位,isDelete等表示该信息是否被删,对于查找历史记录极其重要,缺点数据文件信息会多,但纵观软件开发历程,假删使用居多,因此本次设计采用假删方式。显示用户信息采用真删方式。
2.2 其他考虑
2.2.1. UI界面大小考虑
传统的pos机、收银机等设备都有特定的屏幕大小,因此设计时可以固定UI大小作为界面
2.2.2. 学生信息上下翻页考虑
翻页功能是对于数据显示而言是特有的基本功能,当受到2.2.1 UI界面大小的限制,翻页则增强了人性化。对于翻页功能本质上即清空原先数据再打印特定的个数数据。对于链表而言,即先遍历到特点的位置,然后打印当前页个数据的数据。
2.2.3. 文字背景色考虑
windows 提供控制台字体单文本属性函数:SetConsoleTextAttribute 可进行修改前景色、背景色等,本次项目不使用该功能。
2.2.4. 功能文件分类
文件名 | 文件描述 |
---|---|
fileop | 文件操作:读、写、删除、偏移等 |
commonList | 通用链表操作:遍历、添加、删除等 |
public | 光标操作等 |
public_UI | 通用UI 界面显示 |
stuInfo | 学生结构体信息 |
fuction | 逻辑处理界面 |
2.2.5. 输入输出考虑
对于用户的输入输出通常情况下要做条件限制处理,比如输入密码只能为数字时候才可以显示并且显示为*。此时如果使用scanf 则用户密码等信息一览无余。因此本次采用getch函数进行无回显输入单个字符。当该字符符合条件时候通过printf进行输入到界面上。直到用户回车、取消等退出。
2.2.6. 键盘按键考虑
键盘按键键值存在组合键情况,顾名思义就是由两个keycode表示一个按键,第二个才是真正的键值。如上下左右,因此在翻页时候则可以先判断是否组合键再读取键值进行判断。流程如下:
int firstCode = getch();
if (firstCode == 224) { //组合键第一个键值
realKeyCode = getch();
}
2.2.7. 通用链表考虑
正常设计链表时一个链表代表一种类型的数据的链表,当新增一种数据时候则又要起新链表,代码冗余度高。比如存在教师信息和学生信息两个链表,但对于链表的处理都是一致的,因此可封装统一链表函数。结构如下:
typedef struct commlist {
void *data;
struct commlist *next;
}COMMLIST_ST;
采用无类void 指针指向任意类型,链表模板编写完,直接传入对应的data 数据域即可。
2.2.8. 程序启动考虑
应用程序已具备持久化功能,当程序起机时读取持久化数据,添加进链表,用户选择对应的功能显示对应的数据。对于增删改等实时保存进持久化文件中。
2.2.9. 学生信息显示考虑
学生信息存在删除、查询情况,目前显示界面为了人性化,应把删除的数据不显示出来,等待修改等在显示,因此可设计为两个链表:一个真实存在的用户链表,一个所有数据的链表,真实链表用来显示数据,而所有数据的链表则用来保存更新文件。对于翻页代码逻辑简单,不用判断是否删除而跳开,直接处理真实链表即可。
3. 系统流
3.1 主界面
3.2 查询界面
3.3 添加界面
3.4 删除界面
3.5 修改界面
4. 接口设计
4.1 文件封装
4.1.1 文件打开
/**
* @brief: 文件打开函数(存在则打开,不存在则创建)
* @param: [in] psName 文件名
* @return
* @li NULL 文件打开失败
* @li 非NULL:文件句柄
*/
FILE* pOpenFile(char* psName)
{
... 入参判断 ...
IF (文件存在?)
fopen rb+打开文件
ELSE
fopen wb+ 打开文件
RETURN 文件句柄
}
4.1.2 文件数据写入
/**
* @brief: 将通用链表数据写入文件
* @param: [in] pFile 文件句柄
* [in] stListHead 链表头
* [in] nDataSize单个数据data大小
* @return
*/
void WriteDataFile(FILE* pFile, STCOMLIST* stListHead, int nDataSize)
{
for 遍历 stListHead
IF (数据node不为空node->data 不为空)
fwrite :node->data
}
4.1.3 文件数据读取
/**
* @brief: 将通用链表数据写入文件
* @param: [in] pFile 文件句柄
* [in] 单个数据data大小
* [in] *pFunc 函数指针,特定条件数据才能加入到链表
* @return
* @li NULL 失败
* @li 非NULL:链表头
*/
STCOMLIST* pReadFile(FILE* pFile,int nDataSize, int (*pFunc)(void *pData))
{
文件句柄偏移到开头
WHILE (文件未到eof)//说明还有数据
void*data = 读取1个nDataSize 大小数据
IF ((*pFunc)(data))
加入链表
RETURN 链表头
}
4.1.4 文件数据更新
**
* @brief: 将文件数据更新
* @param: [in] pFile 文件句柄
* [in] nIndex 数据在文件中第几个
* [in] *pData 数据
* [in] nSize 单个数据的大小
* @return
*/
void UpdateFile(FILE* pFile,int nIndex, void* pData, int nSize)
{
调用fseek 偏移到该位置
fwrite 覆盖写入pData
}
4.1.5 文件数据追加
**
* @brief: 将文件数据追加
* @param: [in] pFile 文件句柄
* [in] *pData 数据
* [in] nSize 单个数据的大小
* @return
*/
void AppendFile(FILE* pFile,void* pData, int nSize)
{
调用fseek 偏移到末尾
fwrite 覆盖写入pData
}
4.2 光标封装
4.2.1 设置光标位置
**
* @brief: 光标位置
* @param: [in] nX
* [in] nY
* @return
*/
void SetCurPos(int nX,int nY)
{
GetStdHandle(STD_OUTPUT_HANDLE) 获得控制台句柄 HWD
SetConsoleCursorPosition 调用此函数进行设置x y 坐标
}
4.3 通用链表封装
4.3.1 链表初始化
**
* @brief: 链表初始化
* @param:
* @return
* @li: NULL
* @li: 非空链表头
*/
STCOMLIST *pInitList()
{
malloc 链表头
memset(头部数据)
}
4.3.2 链表添加数据
**
* @brief: 链表添加数据
* @param: [in|out] stListHead 链表头结点
* [in] pData 数据
* @return
*/
void AddList(STCOMLIST* stListHead ,void* pData)
{
malloc 链结点node
node->data = pData;
将node 加入到 链表stListHead 尾部
}
4.3.3 链表查找数据
**
* @brief: 链表查找数据
* @param: [in] stListHead 链表头结点
* [in] pnFunc 比较函数,数据正确则为真
* [in] pcValue 待查找的用户数据
* @return
* @li: -1 未找到
* @li: 找到数据
*/
int nSrchList(STCOMLIST *stListHead,int (*pnFunc)(void *,char * ),char *pcValue)
{
FOR 遍历 stListHead
IF (pnFunc(node->data, pcValue)
return 位置;
return -1;
}
4.4 选择菜单封装
**
* @brief: 选择菜单封装(固定个数选择)
* @param: [in] nx x坐标
* [in] ny y坐标
* [in] nCount 个数
* [in] nSpace 空隙
* [in] psText 显示的光标图标
* [in] nChgPgeFlg ->暂时无用
* [in] nTempChoose->暂时无用
* @return
* @li: -1 未找到
* @li: 找到数据
*/
int nChooseFuc(int nX,int nY,int nCount,int nSpace,char *psText,int nChgPgeFlg,int nTempChoose)
{
WHILE 1
调用SetCurPos 设置光标位置
printf 打印psText 光标图标
IF 上下光标跳转选择
调用printf("\b\b \b")清空前一个打印的数据 (\b就是光标退一格)
设置当前x y的坐标位置,
printf 打印psText 光标图标
IF enter键
返回选择的栏
IF 退出
返回-1
}
5. 参考文献
无
6. 源码路径
上一篇: JavaScript事件委托
下一篇: 使用artDialog来美化页面的提示