这是第一次写的博客,想着用博客来记录下自己码代码的历程,如果有什么不好的地方欢迎各位指正
从2020.4.2到2020.4.23历时22天,算是把课设的内容完成了,后面还花费了一点时间增加了一点东西,算是给课设的一些加分项吧。加起来大概就是花费了一个月左右的时间完成这个课设。
因为课设的代码量较大,所以就将代码文件全部放到GitHub上面了,如果有需要的话可以戳这里
以下是正文内容。
学生证管理系统
问题描述:
简单来说就是,使用C语言对一些特定的数据(学生证信息)实现增删改查+统计的功能。
整体思路
在编写代码之前,为了使逻辑思路不那么混乱,所以先使用process画好了大概的流程图,如下:
接下来为了编写使修改代码的便捷性,选择了之前未曾试过的分开一个个文件编写代码,再用一个头文件将不同文件的代码结合起来。参考了这篇博客,学会了怎么使用自定义头文件。
数据结构
在设计的时候考虑到我们的学生号中已经包含了班级等信息,所以为了代码实现的便捷和查找的方便,使用了数组+双链表的数据结构,数据结构的图如下:
struct CLASS //定义班级结构体//
{
struct STUDENT *tail; //指向班级最后一个学生//
struct STUDENT *head; //指向班级第一个学生//
int num; //存储班级人数//
};
struct STUDENT //定义学生数据结构体//
{
struct STUDENT *last; //指向下一个学生指针//
char name[20]; //学生姓名//
char sex[10]; //学生性别//
char faculty[50]; //学生学院//
char major[50]; //学生专业//
char identity[20]; //学生身份//
char ID[20]; //学生学号//
char card_number[20]; //学生证卡号///
char expiration_date[20];//学生证有效日期//
struct STUDENT *next; //指向下一个学生指针//
};
文件操作
- 因为个人觉得在一个程序中不断打开和关闭文件可能会有点麻烦,在代码实现上好像也不怎么方便(主要是因为懒。。。)
所以只在打开和关闭程序的时候分别进行一次文件读取。当然如果数据量过大的话这种方法还是不可行的。
相关代码文件:getin.c,close_down.c,start_up.c
页面设计
整体的页面采用了上半部分为程序的主要内容,下面留下了3行的空间作为输入指令和提示的空间使用函数print_head()打印整个程序的框架。
//****************************************************************//
//调用此函数,则可以清空屏幕,并打印出程序边框,最后光标在(24,9)//
//****************************************************************//
//边框四角: (24,6),(86,6),(24,28),(86,28)//
//上下中点纵坐标标:18//
//左右中点横坐标:55//
#include"function.h"
void print_head()
{
int i;
system("cls");
gotoxy(24,6);
for(i=0; i<16; i++) //设置上边框//
{
printf("——");
}
gotoxy(47,7);
printf("|学生证管理程序|
"); //程序名字//
gotoxy(47,8);
printf("————————
"); //程序名字下划线//
gotoxy(24,28);
for(i=0; i<16; i++) //设置下边框//
{
printf("——");
}
for(i=6; i<=28; i++) //设置左右边框//
{
gotoxy(23,i);
printf("|");
gotoxy(86,i);
printf("|");
}
gotoxy(24,24); //设置提示栏//
for(i=24; i<=84; i++)
{
printf("-");
}
gotoxy(24,9); //跳转光标//
}
菜单
基本功能
基本功能的实现都是比较基本的内容,没有太多可讲道的地方,可以选择直接跳过
添加学生证信息
相关代码文件:enter.c,getin.c
主要的思路:将信息依次录入到一个临时变量中,在输入完之后用户再输入确定的指令,然后将临时变量传入到输入数据到存储的结构体中的函数中,完成输入。
删除学生证信息
相关代码文件:del.c,ask_id.c
主要思路:先询问用户需要删除的学号,在用户输入正确的学号之后将该学号的信息打印出来,询问用户是否为该学号,之后用户选择确定之后成功删除。
按班级查询
相关代码文件:相关代码:show_class
主要思路:询问班级号,再将班级中的学生信息打印出来,由于使用了双向链表的结构来连接学生的信息,所以用户可以通过按‘←’和‘→’,来浏览学生的信息。
这部分代码感觉挺有意思的,贴在下面。
if(clarr[n].head==NULL) //若该班级头指针为空//
{
print_head();
gotoxy(42,12);
printf("|显示失败,并没有学生在%d班|",n);
clean_tip();
return go_back(); //询问用户下一步操作//
}
else
{
p1=clarr[n].head; //p1指向班级首名学生//
while(1)
{
print_student(*p1); //打印学生信息//
gotoxy(24,25);
printf("显示成功,%d班信息如上,按←和→进行浏览。",n); //提示栏语句//
err=getset();
gotoxy(24,26);
printf("按0退出程序,1返回主菜单。");
while(1)
{
key_down=getch(); //键盘输入,使程序继续显示下一名学生//
if(key_down==-32) //用于消除getch读取方向键时第一次返回的值,该死,不不知道为什么是-32//
{
continue;
}
if(key_down==75)
{
if(p1->last!=NULL)
{
p1=p1->last;
clean_tip(); //清除提示栏//
break;
}
else
{
gotoxy(24,27);
printf("已是第一位学生");
}
}
else if(key_down==77)
{
if(p1->next!=NULL)
{
p1=p1->next;
clean_tip(); //清除提示栏//
break;
}
else
{
gotoxy(24,27);
printf("已是最后一位学生");
}
}
else if(key_down=='0')
{
return 0;
}
else if(key_down=='1')
{
return 1;
}
else
{
error(err);
}
}
}
}
按学号查询
相关代码文件:show_student.c,print_student.c
主要思路:询问用户学号,若存在学号则直接打印学生信息,否则用户可以选择返回主菜单重新查询。
修改学生信息
相关代码文件:modify.c,modify_op.c
主要思路:询问学号→打印信息→选择修改项→输入修改内容→打印修改后信息→选择返回或继续修改。
统计功能
相关代码文件:statistics.c,sta_*,print_sex_table
这部分功能可以选择统计班级人数,总人数,班级性别比例,总性别比例四项。
主要思路:
班级人数在班级结构体中已经进行了存储,所以统计时直接输出就可以
总人数就是对班级人数遍历一遍然后进行统计。
性别比例则是通过对班级中的学生性别进行一遍遍历然后进行统计。由于输入的不确定,所以将性别设置为了3个种类:男(boy),女(girl),其他
有用的辅助函数
这部分包括用来处理程序中一些可能出现的细节问题,以及为了方便程序设计而使用的一些函数。
定位函数
相关代码文件:gotoxy.c、getset.c
gotoxy(int x,int y):这个函数可以使当前光标移动到坐标(x,y)上
POINT getset():这个函数通过返回POINT的值获得当前光标的位置。
两个函数组合起来就可以很方便的对整个程序的框架进行设计。
参考博文:戳这里
输入函数
相关代码文件:super_scan、super_int_scan
- 由于在输入的时候考虑到用户可能会有许多非法输入,比如什么都不输入直接按回车这种情况导致光标会跳出输入框,所以设计了两个输入函数用来处理用户的非法输入。
可以看到如果直接使用scanf输入姓名字符串时,如果用户不输入字符,
直接点击回车,光标此时会跳出程序外面,造成‘穿模’,但是在使用了输入函数之后:
如果直接输入回车,则会在提示栏中提示“输入不能为空”。
代码如下:
第一个用来输入字符以及字符串都可以。
//可以检测输入是否为空,并且没有多余的回车,将输入的整型存在上个函数传递进来的地址中//
#include "function.h"
int super_scan(char temp[])
{
char t;
int i=0;
POINT p;
p=getset();
while(1)
{
if((t=getchar())==EOF)
{
return 0;
}
if(t=='
'&&i==0)
{
gotoxy(24,26);
printf("输入不能为空");
gotoxy(p.x,p.y);
}
else if(t=='
')
{
break;
}
else
{
temp[i++]=t;
}
}
return 1;
}
第二个用来输入整型
//可以检测输入是否为空,并且没有多余的回车,将输入的整型存在上个函数传递进来的地址中//
#include "function.h"
int super_int_scan(int *n)
{
char t;
int i=1;
char a[50]={0};
POINT p;
a[0]='*';
p=getset();
while(1)
{
if((t=getchar())==EOF)
{
return 0;
}
if(t=='
'&&i==1)
{
gotoxy(24,26);
gotoxy(p.x,p.y);
}
else if(t=='
')
{
break;
}
else
{
a[i++]=t;
}
}
*n=transf(a);
return 1;
}
返回函数
相关代码文件:go_back.c
- 由于在完成不同的基本功能之后都有返回主菜单的需求,所以将其包装起来,在使用的时候直接调用。
错误处理函数:
相关代码文件:error.c
- 对于用户的非法输入,我的处理方法是将用户的输入清除,然后对用户进行提醒,让其重新输入。此函数可以实现这一功能。
//该函数用于处理非法输入情况//
#include"function.h"
#pragma comment(lib,"Winmmlib")
void error(POINT point)
{
PlaySound(TEXT("err.wav"),NULL,SND_FILENAME | SND_ASYNC);
gotoxy(49,27);
printf("输入错误,请重试"); //提示栏提示//
gotoxy(point.x,point.y);
printf(" "); //消除上次输入内容//
gotoxy(point.x,point.y);
}
操作成功函数
相关代码文件:success.c
- 对于某些操作做,如录入,删除,用户在操作之后可能感觉不到自己以及成功了,会有点不确定的感觉,此时额外加入一个界面告知用户操作已经成功,提示用户的操作体验。
int success()
{
print_head();
gotoxy(50,15);
printf("—————");
gotoxy(50,16);
printf("|操作成功!|");
gotoxy(50,17);
printf("—————");
return go_back();
}
数据类型转换函数
- 在录入文本信息已经用户输入学号时,由于需要从学号中提取出班级编号进行‘分班’处理,所以使用了一个数据类型转换的函数进行处理。
- 这函数有三个功能,1、从学号中提取出班级编号;2、将文本中的班级人数转换为整型;3、将文本中班级编号转换为整型。
//调用此函数从学号中提取班级整形编码//
//调用此函数也可将文件中班级编码字符串转换为整型//
//调用此函数还可以提取班级总人数//
#include"function.h"
#include <math.h>
int transf(char *a)
{
int i,sum=0,len,j,mul;
if(a[0]=='*') //判断为班级编号标识符//
{
len=strlen(a);
for(i=1;i<len;i++) //将字符串转换为整型并返回//
{
for(j=len-i,mul=1;j>1;j--)
{
mul*=10;
}
sum+=(a[i]-48)*mul;
}
}
else if(a[0]=='&') //判断为班级人数标识符//
{
len=strlen(a);
for(i=1;i<len;i++) //将字符串转换为整型并返回//
{
for(j=len-i,mul=1;j>1;j--)
{
mul*=10;
}
sum+=(a[i]-48)*mul;
}
}
else //判读为学号//
{
for(i=7;i<10;i++) //对学号的去间进行转换//
{
if(i==7)
{
sum+=(a[i]-48)*100;
}
else if(i==8)
{
sum+=(a[i]-48)*10;
}
else if(i==9)
{
sum+=(a[i]-48)*1;
}
}
}
if(sum>0&&sum<10000) //班级编码只在1~9999之间,所以只返回这个区间的值//
{
return sum;
}
else //其他的则返回0//
{
return 0;
}
}
有趣的额外功能
添加音效
- 由于觉得这个程序做完之后还是太过单调,没有什么有趣的地方,用户使用起来可能会觉得很无聊,所以在一些操作上加入了按键的音效,比如删除成功的音效,菜单选择的音效,进出程序,退出程序等音效。为了使用户更有熟悉感,这些音效据采用了Windows的内置系统音效。
添加音效主要使用PlaySound语句。
PlaySound(TEXT("welcome.wav"),NULL,SND_FILENAME | SND_ASYNC );
详情使用方法参考这里
添加加载和退出页面
相关代码文件:welcom.c、outsys.c
- 原本是为了在答辩时用一些一些特别的东西吸引一下老师的注意力,让答辩了一天的老师感到不那么疲惫。所以添加了一些有趣的加载和退出界面。但是因为疫情,又不用答辩了。。。。。。
实现效果:
底部的方块是加载条,以90ms一格的速度加载,配合着打开的音效刚好在音效播放完时加载完毕。
退出界面小人的手会以200ms/帧的速率进行摆动。
实现方法:主要用到了循环和sleep()函数,控制程序运行的速度,这样就能形成加载和运动的感觉。
以下是加载界面代码:
#include "function.h"
#pragma comment(lib,"Winmmlib")
void welcome()
{
int x,y,i;
PlaySound(TEXT("welcome.wav"),NULL,SND_FILENAME | SND_ASYNC );
print_head();
x=46;
y=8;
gotoxy(x,++y);
printf(" ■■■■■■■ ");gotoxy(x,++y);
printf(" ■ ■ ");gotoxy(x,++y);
printf("■ ■ ");gotoxy(x,++y);
printf("■ ■ ■ ■ ");gotoxy(x,++y);
printf("■ ■■ ■■ ■ ");gotoxy(x,++y);
printf("■ ■ ");gotoxy(x,++y);
printf("■ ■ ■ ■ ");gotoxy(x,++y);
printf(" ■ ■■■ ■ ■■ ");gotoxy(x,++y);
printf(" ■ ■ ■##■ ");gotoxy(x,++y);
printf(" ■■■■■■ ■■ ");gotoxy(x,++y);
printf(" ■■■ ■ ");gotoxy(x,++y);
printf("■■■■■■■■■■ ");gotoxy(x,++y);
printf(" ■■■ ");gotoxy(x,++y);
printf(" 欢迎使用本程序 ");
gotoxy(24,25);
printf("加载中");
gotoxy(24,27);
for(i=24;i<55;i++)
{
printf("■");
Sleep(90);
}
gotoxy(24,25);
printf("加载完毕,按任意建进入程序");
getch();
gotoxy(30,30);
}
以下是退出动画代码:
#include"function.h"
#pragma comment(lib,"Winmmlib")
void outsys()
{
PlaySound(TEXT("close.wav"),NULL,SND_FILENAME | SND_ASYNC );
int x,y,i;
print_head();
x=46;
y=8;
gotoxy(x,++y);
printf(" ■■■■■■■ ");gotoxy(x,++y);Sleep(80);
printf(" ■ ■ ");gotoxy(x,++y);Sleep(80);
printf("■ ■ ");gotoxy(x,++y);Sleep(80);
printf("■ ■ ■ ■ ");gotoxy(x,++y);Sleep(80);
printf("■ ■■ ■■ ■ ");gotoxy(x,++y);Sleep(80);
printf("■ ■ ");gotoxy(x,++y);Sleep(80);
printf("■ ■ ■ ■ ");gotoxy(x,++y);Sleep(80);
printf(" ■ ■■■ ■ ");gotoxy(x,++y);Sleep(80);
printf(" ■ ■ ");gotoxy(x,++y);Sleep(80);
printf(" ■■■■■■ ");gotoxy(x,++y);Sleep(80);
printf(" ■■■ ");gotoxy(x,++y);Sleep(80);
printf("■■■■■■■■■ ");gotoxy(x,++y);Sleep(80);
printf(" ■■■ ");gotoxy(x,++y);Sleep(80);
printf(" 感谢使用本程序 ");
gotoxy(24,25);
printf("已成功退出,按任意键继续");
i=1;
while(!_kbhit())
{
i++;
x=64;y=15;
gotoxy(x,++y);
printf(" ■■");gotoxy(x,++y);
printf(" ■##■");gotoxy(x,++y);
printf(" ■■");gotoxy(x,++y);
printf(" ■");gotoxy(x,++y);
printf("■");gotoxy(x,++y);
Sleep(200);
x=64;y=15;
gotoxy(x,++y);
printf(" ");gotoxy(x,++y);
printf(" ");gotoxy(x,++y);
printf(" ");gotoxy(x,++y);
printf(" ");gotoxy(x,++y);
printf(" ");gotoxy(x,++y);
x=64;y=17;
gotoxy(x,++y);
printf(" ■■");gotoxy(x,++y);
printf("■■■##■");gotoxy(x,++y);
printf(" ■■");gotoxy(x,++y);
Sleep(200);
x=64;y=17;
gotoxy(x,++y);
printf(" ");gotoxy(x,++y);
printf(" ");gotoxy(x,++y);
printf(" ");gotoxy(x,++y);
if(i==100)
{
break;
}
}
x=64;y=17;
gotoxy(x,++y);
printf(" ■■");gotoxy(x,++y);
printf("■■■##■");gotoxy(x,++y);
printf(" ■■");gotoxy(x,++y);
getch();
gotoxy(30,30);
}
本次课设的思路和代码基本介绍完成,如果有任何修改意见或问题欢迎各位提出。
这也是本人第一次第一次写博客,以后会继续更新的,撒花花❀❀
``