功能简介
-
链表实现基本的创建和增删改查。
-
成绩分析,链表的冒泡排序。
-
分管理员,老师,学生三端管理,密码的修改,加密。
-
分班级管理老师学生。
-
方框界面,↑↓键控制,ENTER键确认。
界面功能展示
主界面
管理员登录
管理老师信息
管理学生信息
添加学生
成绩查询
具体实现的方法
1.界面
界面实现由↑↓控制选择,ENTER键确认。
这里用到了c语言光标移动函数gotoxy()。
gotoxy()函数定义
void gotoxy(int x, int y)
{
COORD coord = {x, y};
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
}
命令行的列为x轴,行为y轴。
例如使用 gotoxy(10,5); 语句表示将光标移动到第十列,第五行。
先在第一个选项上打印出白框。
然后用getch();无缓冲读取用户的键盘输入。
用if判断,如果读取值的为↓键的ASCII码,则分别调用gotoxy(int x, int y)让光标跳到该白框的上面部分,中间部分和下面的部分的开头,打印一串空格覆盖掉白框,然后再调用gotoxy(int x, int y)让光标移动到下一个选项,分别打印出白框。除此之外还需再定义一个开关值,int key = 1, 然后每读取一次↓键,则key++,记录选中的选项,最后根据key的值再调用不同的函数。
如果判断为↑键,则同理。
还可以利用key值做特殊的判断,如果key的值为1且读入↑键,则让光标跳到最下面的选项打印白框,可以让白框循环移动。或者可以用取余数的办法, 例如一共有5个选项,则可以将每次key的值余6,便可使选框框循环移动。
附上一小段界面代码
按自己的思路写的,有点乱。仅供参考。
void zhujie() {
char c; // 用来读取键盘的输入
int key = 1, x = 38 , y = 7; // key值记录选项, x,y为初始的坐标,可根据自己系统具体而确定。
/* 打印界面 */
printf(" --------------------------------------------------------------------------------------------------------------\n");
printf(" | 学生信息管理系统 |\n");
printf(" --------------------------------------------------------------------------------------------------------------\n");
printf(" | 请选择身份: |\n");
printf(" | |\n");
printf(" | |\n");
printf(" | |\n");
printf(" | ============================ |\n");
printf(" | | 1.管理员 | |\n");
printf(" | ============================ |\n");
printf(" | |\n");
printf(" | 2.老师 |\n");
printf(" | |\n");
printf(" | |\n");
printf(" | 3.学生 |\n");
printf(" | |\n");
printf(" | |\n");
printf(" | 4.退出程序 |\n");
printf(" | |\n");
printf(" | |\n");
printf(" | |\n");
printf(" | |\n");
printf(" | |\n");
printf(" | |\n");
printf(" | ↑↓控制 ENTER 确认 |\n");
printf(" --------------------------------------------------------------------------------------------------------------\n");
c = getch(); // 先从键盘读取一个指令
while( c != 13) { // 如果不是回车循环进行,如果是回车的话直接跳出循环进行下一步判断 。
if (c == 80 && key <= 4) { // 判断如果读取的是↓键
if(key == 4) // 如果已经是最后一个选项,又按了↓键,则跳到最上面
key = 0; // 让key = 0 之后key会自增,就循环记录了第一个选项。
gotoxy(43, y); // 下面是打印空格替换先前的白框,
puts(" ");
gotoxy(43, y+1);
puts(" ");
gotoxy(70, y+1);
puts(" ");
gotoxy(43, y+2);
puts(" ");
if(key == 0) // 这里如果key等于0 则说明要从最后一行跳到第一行,让y等于4跳到第一行。
y = 4;
gotoxy(43, y+3); //打印白框
puts("============================");
gotoxy(43, y+4);
puts("|");
gotoxy(70, y+4);
puts("|");
gotoxy(43, y+5);
puts("============================");
key++; // 因为判断为↓键,所以开关值加一,记录到下一个选项。
y += 3; // 坐标跳到下一个白框位置
gotoxy(0,28);
}
if (c == 72 && key >= 1){ //判断如果是↑键, 与上面同理。
if(key == 1)
key = 5;
gotoxy(43, y);
puts(" ");
gotoxy(43, y+1);
puts(" ");
gotoxy(70, y+1);
puts(" ");
gotoxy(43, y+2);
puts(" ");
if(key == 5)
y = 19;
gotoxy(43, y-1);
puts("============================");
gotoxy(43, y-2);
puts("|");
gotoxy(70, y-2);
puts("|");
gotoxy(43, y-3);
puts("============================");
key--;
y -= 3;
gotoxy(0,28);
}
c = getch(); //最后再读取一次键盘,循环判断,直到是回车(ASCII码为13)为止。
}
if (key == 1) denglu1(); // 这里跳出while 根据开关量决定进入哪个函数。
if (key == 2) denglu2();
if (key == 3) denglu3();
if (key == 4) {
gotoxy(0, 80);
exit(0) ;
}
}
其他界面的实现可参考这个,都用的是gotoxy()函数。
2.多文件管理
可建立一个工程,分多文件来写管理系统,每个模块写一个文件,并添加一个头文件,方便管理查询。
分多文件管理
头文件
3.链表
数据结构
/*学生链表*/
struct ms { // 学生信息和成绩 (数据域)
char name[100];
char sex[10];
char number[10];
int lisan;
int math;
int english;
int c;
int sum;
int ban;
char m[10];
float avrg;
};
typedef struct lian { // 链表结构
struct ms item ; // 数据域 这里分开两个域便于值的交换
struct lian * next ; // 指针域
} lian;
/* 老师链表*/
struct tems { // 老师信息
char name[100];
char z[21];
char m[21];
int ban;
};
typedef struct telian { // 老师链表
struct tems teitem;
struct telian * next;
}telian;
创建链表
/*返回结构指针的函数,返回创建的结构指针。也可用结构指针的指针当做函数的参数来传递值 */
lian * creat ()
{ //创建学生链表 带头节点
struct lian * current = NULL;
current = (struct lian *)malloc(sizeof(struct lian));
current->next = NULL;
return current;
}
telian * creat2 () { //创建老师链表 带头结点
struct telian * current = NULL;
current = (struct telian *)malloc(sizeof(struct telian));
current->next = NULL;
return current;
}
按成绩排序查询
这里用的是优化后的链表的冒泡排序。这里按照sum(总分)进行排序,更换sum即可以按其他科目排序。
void sortsum() { //冒泡排序链表
struct lian * current = head->next; // 带头结点的链表,head->next 为第一个节点
/* 这里头指针head是在所有函数外声明的,具有文件作用域,可以再任意函数里直接使用 */
struct lian t; // 用于赋值交换
int ch = 1 , y=7; // ch为优化后的冒泡的开关量。
if ( current == NULL ) // 判断链表是否为空
{
/* 这里打印提示信息, 说明链表没有成员。*/
}
while (ch) { //冒泡排序,ch判断有无交换。
ch = 0;
while (current->next != NULL) { // 节点遍历比较
if ( current->item.sum < current->next->item.sum)
{
t.item = current->item; // 结构可直接赋值, 交换
current->item = current->next->item;
current->next->item = t.item ;
ch = 1; // 标记交换,还需继续循环
}
current = current->next ;
}
}
}
4. 文件存储
这里是用fwrite和fread以二进制一次性存储链表和读取链表,其他的增删改查操作都是用链表实现的。
void cun() {
struct lian * current = head->next;
FILE * fp;
fp = fopen("lian.txt","w");
if (fp == NULL) {
}
while (current != NULL) {
fwrite(current,sizeof(struct ms),1,fp); //二进制依次写入
current = current->next;
}
fclose(fp);
}
}
void cun2() {
struct telian * current = head2->next;
FILE * fp;
fp = fopen("laolian.txt","w");
if (fp == NULL) {
}
while (current != NULL) {
fwrite(current,sizeof(struct tems),1,fp);
current = current->next;
}
fclose(fp);
}
void du() {
struct lian * prev = head, * current = NULL;
int shu = 0;
FILE * fp;
fp = fopen("shu.txt","r");
/* 这个文件存的是链表节点,也就是成员的数量,因为fread读取时要输入数量,所以单独存一下。*/
if (fp == NULL) {
/*这里写打开失败的提示*/
}
fscanf(fp,"%d",&shu);
fclose(fp);
fp = fopen("lian.txt","r"); // 打开存链表的文件
if (fp == NULL) {
/* 打开失败的提示 */
}
while(shu--){ // 读的次数。
current = (struct lian *)malloc(sizeof(struct lian));// 添加节点。
fread(current,sizeof(struct ms),1,fp); // 读取,一次读取一个节点。
prev->next = current;
current->next = NULL;
prev = current;
}
fclose(fp);
}
5. 加密
这里的管理员不能从程序里更改,需要自行更改文件目录里的文件。
一个简单的根据ASCII码加密解密的函数
注: 自己实现的并不是很规范,可以去网上了解MD5加密算法。
void jiami(char x[]) {
for (int i = 0; i < strlen(x); i++) {
x[i] = x[i] + 10 + i;
}
}
void jiemi(char x[]) {
for (int i = 0; i < strlen(x); i++) {
x[i] = x[i] - 10 - i;
}
}
单独写一个程序跑一下加密的函数,然后把结果复制到文件夹里的对应文件里就行了。
账号密码用空格隔开
这里管理员默认账号密码都为admin