C项目实践--图书管理系统(3)
接下来将要实现用户管理模块的相关功能,用户管理模块的主要功能包括增加用户,查找用户以及保存用户等功能,查找用户时,如果查找成功,充许对查找到用户进行更新或删除操作。如果查找不成功,则给出相应的提示信息。
打开user.c文件, 首先包含要用到的头文件,同时还需要定义一个常量用来表示存储用户信息的文件,定义一个单链表的头节点,并初试化为空。具体实现如下:
#pragma warning(disable:4996)
//Header Info
#include <stdio.h>
#include <stdlib.h>
#include <string.h> //使用memset函数 memset是字符串处理函数
#include <conio.h> //使用kbhit()函数 来星号显示密码
#include "user.h"
#include <assert.h>
#define USER_FILE "user.dat" //表示用来存储用户信息的文件
user* first_user = NULL; //定义一个头节点, 初始化为NULL
下面来实现主要的处理函数:
1.新增用户
函数名称:add_user
函数功能:新增一个用户信息,在管理员操作菜单中,选择6调用本函数,由于用户名不充许重复,所以用户输入用户名和密码后,需要在用户链表中查询用户名是否重复。
处理流程:1创建一个用户节点new_user. 2初始化new_user. 3.调用函数input_user(), 提示用户输入用户信息,为new_user赋值。4.调用函数find_user(),查找用户名是否存在,如果新增用户名不存在,则增加该用户,否则提示相应的信息。具体实现如下:
//新增用户信息
void add_user()
{
char try_again = 'Y';
user* p = NULL;
user* new_user= (user*)malloc(sizeof(user));
assert(new_user != NULL);
while(try_again == 'Y' || try_again == 'y')
{
memset(new_user,0,sizeof(user));
printf(">增加用户信息...\n");
input_user(&(new_user->ui));
//对比用户名是否已存在
p = find_user(new_user->ui.username);
if(NULL == p)
{
p = get_last_user();
p->next = new_user;
break;
}
printf(">用户[ %s ]已存在.重新输入?(y or n ):",new_user->ui.username);
fflush(stdin);
try_again = getchar();
if(try_again !='y' && try_again !='Y')
{
free(new_user);
}
}
}
2.查找用户
函数名称:search_user
函数功能:查找用户信息,在管理员操作菜单中选择7调用本函数,提示用户输入要查询的用户名,在用户链表中进行查找,如果查找成功,显示该用户信息,并提示按d/D键删除该用户信息,按u/U键更新该用户信息。如果查找不成功,则给出提示信息。
处理流程:1提示用户输入要查询的用户名,根据用户名进行查找。2调用函数find_user()查找输入的用户名信息,若存在,则将用户信息赋值给节点p。3如果没有找到,则给出提示信息,如果找到了则调用show_user()函数,显示该用户信息,并提示用户可以对该用户信息进行删除或更新操作。具体实现如下:
void search_user()
{
char input_char = 'Y';
char username[MAX_USERNAME] = {0};
user* p = NULL;
while(input_char == 'Y' || input_char == 'y')
{
printf(">查找用户信息...\n");
printf(">请输入用户名(最大长度为 %d):",MAX_USERNAME);
fflush(stdin);
scanf("%s",username);
p = find_user(username);
if(p == NULL)
{
printf(">未找到用户:%s的信息.继续查找?(y or n)",username);
fflush(stdin);
input_char = getchar();
continue;
}
show_user(&(p->ui));
printf(">查找成功!按d/D键删除该用户,按u/U键更新该用户信息,按其它键返回!");
fflush(stdin);
input_char = getchar();
if(input_char == 'd'||input_char == 'D')
{
delete_user(p);
}else if(input_char =='U' || input_char == 'u')
{
update_user(p);
}
printf(">继续查找其它用户吗?(y or n):");
fflush(stdin);
input_char = getchar();
}
}
3.删除用户信息
函数名称:delete_user
函数功能:管理员在查找到用户成功时,充许对其找到的用户信息进行删除操作。
处理流程:提示用户是否确认要删除该用户信息,如果输入"y"或"Y", 则删除该用户信息,否则继续查找。具体实现如下:
void delete_user(user* p)
{
char input_char = 'N';
user* previous = NULL;
printf(">确认要删除用户 [%s] 吗?(y or n):",p->ui.username);
fflush(stdin);
input_char = getchar();
if(input_char == 'y' || input_char == 'Y')
{
previous = get_previous_user(p);
previous->next = p->next;
free(p);
p = NULL;
}
}
4.更新用户信息
函数名称:update_user
函数功能:管理员在查找到用户成功时,充许对找到的用户信息进行更新操作。
处理流程:1创建一个用户信息new_p, 2调用函数input_user()往new_p中输入用户信息。3在用户链表中查找该用户是否存在,如果输入的用户名已存在,并且这个用户名不是原来的p,系统给出相关提示信息,等待下一步操作,否则进行更新操作。具体实现如下:
//更新用户信息
void update_user(user* p)
{
char input = 'y';
user* exist_p = NULL;
user_info* new_p = (user_info*)malloc(sizeof(user_info));
assert(new_p != NULL);
while(input == 'y'|| input == 'Y')
{
memset(new_p,0,sizeof(user_info));
input_user(new_p);
//查找当前更新时输入的用户名是否已经存在
exist_p = find_user(new_p->username);
//如果单链表中存在这个用户名,且新更新的用户名和刚才被更新的用户名不相等
//则表示不能进行更新
if(exist_p != NULL && exist_p != p)
{
printf(">用户[%s] 已存在.请选用其它用户名.\n",exist_p->ui.username);
printf(">重新输入?(y or n):");
fflush(stdin);
input = getchar();
}else{
strcpy(p->ui.username,new_p->username);
strcpy(p->ui.password,new_p->password);
p->ui.user_type = new_p->user_type;
break;
}
}
free(new_p);
}
5.保存用户信息
1.函数名称:save_users
函数功能:管理员操作菜单中选择8调用本函数,用来保存用户信息,函数中通过调用save_users_to_file()函数将用户信息保存到文件中,并给出提示信息,具体实现如下:
void save_users()
{
save_users_to_file();
printf(">保存成功!按任意键返回...");
fflush(stdin);
getchar();
}2.保存用户信息到文件
void save_users_to_file()
{
FILE* fp = fopen(USER_FILE,"wb");
user* p = first_user;
while(p != NULL)
{
fwrite(&(p->ui),sizeof(user_info),1,fp);
fseek(fp,0,SEEK_END);
p = p->next;
}
fclose(fp);
}
上面完成了用户管理模块的主要功能函数,但是还需要一些辅助函数才能完整的实现用户管理模块的功能,这些辅助函数实现如下:
1.用户模块初始化
函数名称:init_user
函数功能:设定默认的用户名为admin, 密码为123,权限为管理员,如果存储用户信息的文件不存在则创建一个,如果创建失败则给出提示信息。具体实现如下:
void init_user()
{
FILE* fp = NULL;
user_info default_admin;
strcpy(default_admin.username,"admin");
strcpy(default_admin.password,"123");
default_admin.user_type = ADMIN;
fp = fopen(USER_FILE,"r");
if(NULL == fp)
{
fp = fopen(USER_FILE, "wb");
fwrite(&default_admin,sizeof(user_info),1,fp);
}
fclose(fp);
}
2.加载用户信息
函数名称:load_users
函数功能:从用户文件中将用户信息加载到用户链表中。具体实现如下:
void load_users()
{
user* u = NULL;
user* last = NULL;
FILE* fp = NULL;
int count = 0;
u = (user*)malloc(sizeof(user));
memset(u,0,sizeof(user));
u->next = NULL;
//以只读方式打开二进制文件USER_FILE
fp = fopen(USER_FILE , "rb");
//将文件指针置为文件开始处
fseek(fp,0,SEEK_SET);
//从文件中逐个读取用户信息,并把它们存放到用户链表中
while(fread(&(u->ui),sizeof(user_info),1,fp) == 1)
{
if(first_user == NULL)
{
first_user = u;
}else{
last = get_last_user();
last->next = u;
}
count++;
//以user_info长度为单位相对文件开始位置
//偏移文件指针的位置
fseek(fp,count*sizeof(user_info),SEEK_SET);
u = (user*)malloc(sizeof(user));
memset(u,0,sizeof(user));
u->next = NULL;
}
free(u);
u = NULL;
fclose(fp);
}
3.判断用户类型
函数名称:login
函数功能:用户登录功能,如果输入正确,返回用户类型。
处理流程:1.提示输入登录名和密码,2.在用户链表中查找是否存在该登录名,若不存在,则给出提示信息,否则继续判断密码是否匹配,如果匹配成功,则返回用户权限类型,否则给出提示信息。具体实现如下:
USER_TYPE login()
{
char username[MAX_USERNAME] = {0};
char password[MAX_PASSWORD] = {0};
int j = 0;
char try_again = 'Y';
user* p = NULL;
while(try_again == 'y' || try_again == 'Y')
{
printf("请输入用户名:");
fflush(stdin);
scanf("%s",username);
printf(" 请输入密码:");
fflush(stdin);
//scanf("%s",password);
//密码输入处理:输入一个字符,回显一个星号
getLine(password,MAX_PASSWORD);
//printf("\n");
p = find_user(username);
if(p == NULL)
{
printf("\n用户名输入有误.请重试!\n");
}else if(strcmp(p->ui.password,password) != 0)
{
printf("\n密码输入有误.请重试!按任意键继续...\n");
}else{
return p->ui.user_type;
}
printf(">重新输入吗?按其它键退出系统(y or Y 继续):");
fflush(stdin);
try_again = getchar();
}
exit(0);
}
//密码输入处理,输入一个字符就回显一个星号
char *getLine(char *buf,int len)
{
int i = 0;
char ch;
fflush(stdin);
/*while(i<len-1 && '\n' !=(ch = kbhit()))*/
while(i<len-1)
{
//ch 第一次接收到一个字符之后, 以后一直存在这个字符,所以输入失效
/*if('\0' != ch){*/
if(0 != kbhit()){
//kbhit()只能检测到键盘是否有键位按下,如果有则返回非零值,否则返回零
//返回的这个非零值并非代码键位的ASCII值,所以返回值不能用
//getchar()用来接收输入的字符串,输入一个就回显一个直到遇到空格或回车
//才停止接收输入
//ch = getchar();
ch = getch();
//c中 \r 才是代表 回车符
if(ch == '\r') break;
putchar('*');
buf[i] = ch;
++i;
}
}
buf[i] = '\0';
return buf;
}
4.清除用户链表
函数名称:clear_users
函数功能:从内存中清除用户链表中的内容。具体实现如下:
void clear_users()
{
user* p = NULL;
//从头节点往后一个个清空
while(first_user != NULL)
{
if(first_user->next != NULL)
{
p = first_user;
first_user = first_user->next;
free(p);
p = NULL;
}else{
free(first_user);
first_user = NULL;
}
}
}
5.取用户链表中的最后一个节点
函数名称:get_last_user
函数功能:取得用户链表中的最后一个节点并返回该节点指针。具体实现如下:
user* get_last_user()
{
user* p = first_user;
while((NULL != p) &&(p->next != NULL))
{
p = p->next;
}
return p;
}
6.取某节点的前驱节点
函数名称:get_previous_user
函数功能:取得节点p的前驱节点并返回该节点的指针。具体实现如下:
user* get_previous_user(user* p)
{
user* previous = first_user;
while(previous != NULL)
{
if(previous->next == p)
{
break;
}
previous = previous->next;
}
return previous;
}
7.显示用户信息
函数名称:show_user
函数功能:显示用户信息。具体实现如下:
void show_user(user_info* info)
{
printf("------------------------------------------\n");
printf(" 用户名:%s\n",info->username);
printf(" 密码:%s\n",info->password);
printf("用户类型:%s\n",info->user_type == ADMIN ? "admin" : "user");
printf("\n");
}
8.输入用户信息
函数名称:input_user
函数功能:提示用户输入用户相关信息,具体实现如下:
void input_user(user_info* info)
{
printf(">请输入用户名(最大长度为 %d):",MAX_USERNAME);
fflush(stdin);
scanf("%s",info->username);
printf(">请输入密码(最大长度为 %d):",MAX_PASSWORD);
fflush(stdin);
scanf("%s",info->password);
printf(">请输入用户类型(%d 是管理员,%d 是普通用户):",ADMIN,USER);
fflush(stdin);
scanf("%d",&(info->user_type));
}
9查找一个用户
函数名称:find_user
函数功能:从用户链表中查找指定的用户名是否存在,若存在则返回该用户节点指针,否则返回NULL。具体实现如下:
user* find_user(char* name)
{
user* p = first_user;
int is_found = 0;
while(p != NULL)
{
//字段比对,如果相同则strcmp返回0
if(strcmp(p->ui.username,name) == 0)
{
is_found = 1;
break;
}
p = p->next;
}
if(is_found)
{
return p;
}
return NULL;
}
到此为止,图书管理系统的三大功能模块:登录管理模块,图书信息管理模块和用户信息管理模块已基本实现完毕,接下来是系统操作过程分析,具体情况请转入:<< C项目实践--图书管理系统(4)>>