登录 / 注册
首先定义一个结构体存储用户的账号和密码,如下:
typedef struct user {
char username[100]; //用户名
char password[100]; //密码
}user;
写一个函数用来注册,其中主要用运用文件存储用户的账号密码。
另,在注册用户名时,还读取一遍文件判断该用户名是否已经被注册过,若被注册过则标记。如下
while (!feof(fp)) {
fscanf(fp,"%s %s\n",a.username, a.password);
if (strcmp(a.username,name) == 0) {
flag = 1;
break;
}
}
在写登录页面时,主要运用对之前用来存储用户账号密码的文件的读取,如果输入的用户名和密码与文件中存储的账号密码相匹配则进行下一步,反之则退出。
如果存储用户的帐号和密码的文件为空也要做出相关操作:
fp = fopen("StudentAccounts.txt","r");
printf("================================\n");
printf("\n\n 学生登录 \n\n");
if (fp == NULL) {
printf("\n\n\t ##### 文件为空!请先注册!#####");
Sleep(800);
system("cls");
RegisterFace(); //转到注册界面
}
fclose(fp);
另一个重点,在登陆时对密码的加密处理也是一个小难点,其中用到了getch()函数,其用途:从控制台读取一个字符,但不显示在 屏幕 上。并且用getch()函数需要用到头文件conio.h 。
关于getch()函数:
所在头文件: conio.h
函数用途:从控制台读取一个字符,但不显示在 屏幕 上
函数原型:int getch(void)
返回值:读取的字符
在不同平台,输入回车,getch()将返回不同数值,而getchar() 统一返回10(即 \n )
1)windows平台下ENTER键会产生两个 转义字符 \r \n,因此getch返回 13 ( \r )。
2)unix 、linux系统中ENTER键只产生 \n ,因此getch返回10 ( \n )。
3)MAC OS中ENTER键将产生 \r ,因此getch返回13( \r )。
加密代码,如下:
while ((ch = getch()) != '\r') { //每次输入的字符不被显示
if (ch == '\b') {
if (i != 0) {
printf("\b \b"); //回退
i--;
}
}
else {
putchar('*'); //将输入的字符转化为 *
code[i++] = ch; //code[]字符数组里存储的就是密码
}
}
code[i] = '\0'; //收尾,即在密码字符串末尾加上\0
文件的读取
因为课设文件里已经把五个班的信息已经给了我,所以我从一开始就在思考该如何把这五个班信息的文件读取在链表里,然后再便于后续的增删改查的操作
首先,需要定义一个链表,便于后续将文件中每一个人的成绩放进链表,如下
typedef struct node {
char name[50]; //姓名
char number[50]; //学号
char Class[50]; //专业班级
int Chinese; //语文成绩
int Math; //数学成绩
int English; //英语成绩
struct node *next;
}Node;
(将链表的头指针设为全局变量,便于后面的使用)
Node* head = (Node*)malloc(sizeof(Node));
文件里从第一个人的信息开始进行读入,注意是按文件的顺序依次被读取,每次被读取的信息都暂时保存下来再一次放进链表,当每个同学对应的信息在该节点被保存好后再指向下一个节点。
代码如下
char name1[50]; //姓名
char number1[50]; //学号
char Class1[50]; //专业班级
int Chinese1; //语文成绩
int Math1; //数学成绩
int English1; //英语成绩
FILE *fp;
fp = fopen("class1.txt","rb"); //以只读方式打开当前目录下的.txt
if (fp == NULL) {
printf("不存在打开文件\n");
exit(0); //终止程序
}
int i = 0;
while (!feof(fp)) {
fscanf(fp,"%s %s %s %d %d %d",name1, number1, Class1, &Chinese1, &Math1, &English1);
i++;
}
fclose(fp);
FILE *FP;
FP = fopen("class1.txt","rb"); //以只读方式打开当前目录下的.txt
if (FP == NULL) {
printf("无法打开文件\n");
exit(0); //终止程序
}
int b = i-1;
int j = 1;
Node* p = head;
while (!feof(FP)) {
fscanf(FP,"%s %s %s %d %d %d",name1, number1, Class1, &Chinese1, &Math1, &English1);
Node* pnew = (Node*)malloc(sizeof(Node));
strcpy(pnew -> name, name1); //把后者的内容拷贝到前者中
strcpy(pnew -> number, number1); //把后者的内容拷贝到前者中
strcpy(pnew -> Class, Class1);
pnew -> Chinese = Chinese1;
pnew -> Math = Math1;
pnew -> English = English1;
pnew -> next = NULL; //插入新的节点
p -> next = pnew;
p = pnew;
if(j == b)
break;
j++;
}
fclose(FP); //关闭文件
后续:
当我能成功的将文件里的信息读入后,我觉得就成功了一半了(可以先写一个display()函数浏览所有人的信息,打印出来,判断是否真正的信息读入完全)。此后就可以正常的对链表进行增删改查了,因为在读入时已经将每个人的信息构成了一个链表,只需要对此时的链表进行操作就好了。注意在对链表操作完后,此时链表里更改过的信息还是没有保存在文件中,还需要写一个保存到文件的save()函数,这个比较易操作。如下
void save() {
Node *p;
FILE *fp;
if ((fp = fopen("class1.txt","wt")) == NULL) {
exit(1);
}
for (p = head -> next; p != NULL; p = p -> next) {
fprintf(fp,"%s %s %s %d %d %d\n",p -> name, p -> number, p -> Class, p -> Chinese, p -> Math, p -> English);
}
fclose(fp);
}
不算重点的点:
我在写菜单页面时,一开始交互页面用的是switch语句,每个页面都用的这种方法,至到基本写完整个系统。在对系统进行复查的时候,进入菜单页面,如下
我的想法是输入(0 ~ 5)时进入如图所示对应的页面,而输入其他按键时会提示输入错误重新输入。但当我输入一个除数字外的英文字母或其他字符时,程序会崩。我认为是定义变量key时用字符型会规避这个问题,但改正后如果输入的是一个字符串,那么他只能读取字符串的首字符(也因为我一开始定义的是单字符,想过用fflush(stdin)清空缓冲区,但是如果我输入1111程序会认为我输入的是1,并且成功进入1.学生登录的页面。并且switch语句无法对字符串进行操作),此时我体验到了if else语句的真香,将switch中每一个情况用if else代替,在判断时用字符串判定函数strcmp(),该函数既能对单字符也能对字符串进行判定,很好用。在更正后,成功的规避了所有bug。
总结
总的来说,我认为对文件读写比较关键,在能正常对文件信息读取后,后续操作就显得简易了许多。后续链表的增删改查,对链表存储信息的分析都是建立在文件信息内容能一字不落的读入的前提下。为期两周的时间,我对文件知识有了思考,对链表进行了复习回顾,巩固了增删改查的能力,普及到了另外的函数如getch()等,自己也能对存在的bug一步一步的改正,达到自己想要的结果。