https://gitee.com/x-0ne/library_system.git
1.设计模块及其功能
2.设计函数名(见名知意)
3.工具函数(每个模块基本都需要用到的函数)
// 缓冲区清理
void clear_stdin(void)
{
stdin->_IO_read_ptr = stdin->_IO_read_end;
}
// 带休眠时间的字符串输出 显示延时
void put_str(const char* str,float sec)
{
printf("%s\n",str);
usleep(sec*1000000);
}
// 任意键继续
void anykey_continue(void)
{
clear_stdin();
puts("按任意键继续...");
getch();
}
// 获取指令
char get_cmd(char start,char end)
{
puts("----------------");
printf("请输入指令:");
clear_stdin();
for(;;)
{
char cmd = getch();
if(start <= cmd && cmd <= end)
{
printf("%c\n",cmd);
return cmd;
}
}
}
// 安全的字符串输入
char* get_str(char* str,size_t size)
{
if(NULL == str || 1 >= size)
return NULL;
// 清理输入缓冲区
clear_stdin();
// 以安全的方式读取size-1个字符
fgets(str,size,stdin);
// 计算字符串的长度
size_t len = strlen(str);
if(str[0]=='\n') return NULL;
// 判断最后一个字符是否是\n
if('\n' == str[len-1])
// 删除'\n'
str[len-1] = '\0';
else
// 清理输入缓冲区
while('\n' != getchar());
return str;
}
// 用于初始化ID,首次调用有效
void init_id(const char* path,unsigned int id)
{
if(0 == access(path,F_OK))
return;
FILE* fp = fopen(path,"w");
if(NULL == fp)
{
put_str("初始化ID失败,请检查path路径参数!",1);
return;
}
fwrite(&id,sizeof(id),1,fp);
fclose(fp);
}
// 从指定的路径读取ID,自加后再回写
unsigned int get_id(const char* path)
{
FILE* fp = fopen(path,"r+");
if(NULL == fp)
{
put_str("获取ID失败,请检查path参数!",1);
return -1;
}
unsigned int id;
fread(&id,sizeof(id),1,fp);
id++;
rewind(fp); //指针重新放到文件的开头
fwrite(&id,sizeof(id),1,fp);
fclose(fp);
return id-1;
}
// 安全的密码输入 输入长度超过size则返回空指针
char* get_pwd(char* passwd,size_t size)
{
clear_stdin();
size_t cnt = 0;
while(cnt>=0)
{
passwd[cnt] = getch();
if(10 == passwd[cnt])
{
break;
}
else if(127 == passwd[cnt])
{
if(cnt > 0)
{
cnt--;
printf("\b \b");
}
}
else
{
cnt++;
printf("*");
}
}
if(cnt>size-1) return NULL;
passwd[cnt] = '\0';
printf("\n");
return passwd;
}
4.宏定义
#define Display 15 //一页显示多少个
#define PATH_MAX 30 //路径长度30
#define PASSWD_LEN 11 //密码长度10
#define MANAGER_MAX 100 //普通管理员上限
#define READER_MAX 1000 //读者上限
#define BOOK_MAX 5000 //图书上限
#define Delay_Time 1.5 //延时时间
#define MANAGER_ID 2023000 //管理员id初始化
#define READER_ID 11111 //读者id初始化
#define BOOK_ID 100000 //图书id初始化
#define SUPER_PATH ".super.dat" //超级管理员信息文件路径
#define MANAGER_PATH ".manager.dat" //普通管理员信息文件路径
#define READER_PATH ".reader.dat" //读者信息文件路径
#define BOOK_PATH ".book.dat" //图书信息文件路径
#define MANAGER_ID_PATH ".manager_id.dat" //普通管理员id存放路径
#define READER_ID_PATH ".reader_id.dat" //读者id存放路径
#define BOOK_ID_PATH ".book_id.dat" //图书id存放路径
#define MANAGER_Export_File "manager.txt" //管理员导出文件
#define READER_Export_File "read.txt" //读者导出文件
#define BOOK_Export_File "book.txt" //图书导出文件
.xxxx.dat 是隐藏的二进制文件
5.部分函数
1.超级管理员第一次登陆系统
int load_super(void)
{
sup=calloc(1,sizeof(Str_super));
FILE* fp = fopen(SUPER_PATH,"r");
if(NULL == fp)
{
//fp = fopen(SUPER_PATH,"w");
printf("第一次登录超级管理员!\n");
printf("请输入管理员的姓名:");
if(!get_str(sup[0].name,sizeof(sup[0].name)))
{
put_str("\n用户名空,设置失败!",Delay_Time);
return 0;
}
char passwd1[Length],passwd2[Length];
printf("请输入初始密码(%d位以内):",PASSWD_LEN-1);
if(!get_pwd(passwd1,PASSWD_LEN))
{
put_str("\n密码长度过长,初始密码设置失败!",Delay_Time);
return 0;
}
printf("请再次输入初始密码:");
get_pwd(passwd2,PASSWD_LEN);
if(strcmp(passwd1,passwd2))
{
put_str("\n两次输入的密码不符,设置失败!",Delay_Time);
sup=NULL;
return 0;
}
memcpy(sup[0].pwd,passwd1,sizeof(sup[0].pwd));
fp = fopen(SUPER_PATH,"w");
fwrite(sup,sizeof(Str_super),1,fp);
put_str("超级管理员设置成功!",Delay_Time);
}
else
fread(sup,sizeof(Str_super),1,fp);
fclose(fp);
fp=NULL;
return 1;
}
2.加载和保存管理员信息
void load_manager(void) //加载管理者数据
{
FILE* fp = fopen(MANAGER_PATH,"r"); //以只读打开该文件 文件必须存在
if(NULL == fp) //如果失败 就创建一个
{
fp = fopen(MANAGER_PATH,"w"); //这里还需要判断一下是否是空指针
return;
}
Manager_cnt = fread(man,sizeof(str_manager),MANAGER_MAX,fp);
//size_t(返回成功读取的对象个数) fread( void *buffer(指向要读取的数组中首个对象的指针), size_t size(每个对象的大小), size_t count(要读取的对象个数), FILE *stream(目标文件指针) );
fclose(fp);
fp=NULL;
}
void save_manager(void) //保存管理者数据
{
FILE* fp = fopen(MANAGER_PATH,"w");//以只写打开文件,若文件存在则文件长度清为零,即内容消失;若文件不存在则创建该文件。
if(NULL == fp)
{
put_str("保存管理员信息失败!",Delay_Time);
return;
}
//put_str("保存管理员信息成功!",1);
fwrite(man,sizeof(str_manager),Manager_cnt,fp);
fclose(fp);
}
3.翻页显示管理员名单
static void list_manager_super()
{
if(0 == Manager_cnt)
{
put_str("还没有管理员哦!",Delay_Time);
return;
}
for(int i=0; i<Manager_cnt/(Display+1)+1; i++) //i表示有几页
{
system("clear");
printf(" id 姓名 工作岗位");
printf("\n");
for(int j=0;j<Display;j++) //Display表示每页显示多少个
{
if(man[i*Display+j].id==0) break;
printf("%-7u %-16s\t",
man[i*Display+j].id,
man[i*Display+j].name);
if('t'==man[i*Display+j].job) printf("图书管理员\n");
else printf("读者管理员\n");
}
printf("j:上一页 k:下一页 l:退出 -----当前页数:%d-----\n",i+1);
switch(get_cmd('j','l')) //这里用到了工具函数
{
case 'k': i=(i==Manager_cnt/(Display+1))?(i-1):i; break;
case 'j': i=(i==0)?-1:(i-2); break;
default: return;
}
}
}
4.读者预约图书
书的状态book_state 1:无预约在馆 2:有预约在馆 3:无预约离馆 4:有预约离馆
void booking_book_reader(void)
{
if((rea[ord_rea].borrow_book_num >=3)&&(rea[ord_rea].booking_book_num >=3)) //自己借阅数,预约数都大于等于3
{
put_str("借阅图书和预约图书均已达上限!!!",Delay_Time);
return;
}
unsigned int book_id;
int i;
printf("请输入需要预约的图书id:");
scanf("%u",&book_id);
for(i=0;i<Book_cnt;i++)
{
if(book_id == boo[i].id) //系统里存在这个id的书
{
for(int j=0;j<3;j++) //for(int j=0;j<3;j++) //判断是否在自己借的书当中
{
if(rea[ord_rea].borrow_book_id[j]==book_id)
{
put_str("此图书已被自己借阅,不用预约",2);
return;
}
}
for(int j=0;j<3;j++) //判断是否在自己预约的书当中
{
if(rea[ord_rea].booking_book_id[j]==book_id)
{
put_str("此图书已被自己预约,不用重复预约",2);
return;
}
}
if((boo[i].book_state==1)&&(rea[ord_rea].borrow_book_num < 3)) // 无预约在馆 自己借书数小于3,直接借走
{
printf("借书数不足3本,无需预约,已成功借阅该书本\n");
for(int j=0;j<3;j++) //更新借的情况
{
if(rea[ord_rea].borrow_book_id[j]==0)
{
rea[ord_rea].borrow_book_id[j] = book_id;
rea[ord_rea].borrow_book_num++;
//printf("读者信息已更新\n");
break;
}
}
for(int j=0;j<3;j++) //更新预约的情况
{
if(rea[ord_rea].booking_book_id[j]==book_id)
{
rea[ord_rea].booking_book_id[j] = 0;
rea[ord_rea].booking_book_num--;
//printf("读者信息已更新\n");
break;
}
}
boo[i].borrow_book_cnt ++;
boo[i].borrowe_id = acc_rea;
boo[i].booking_id = 0;
boo[i].book_state =3; //无预约离馆
//put_str("书本信息已更新!",3);
_find_book_number(i);
show_people_reader();
return;
}
else if((boo[i].book_state==1||boo[i].book_state==3)&&(rea[ord_rea].booking_book_num < 3))// 无预约在馆 无预约离馆 自己预约数小于3
{
for(int j=0;j<3;j++) //更新预约的情况
{
if(rea[ord_rea].booking_book_id[j]==0)
{
rea[ord_rea].booking_book_id[j] = book_id;
rea[ord_rea].booking_book_num++;
//printf("读者信息已更新\n");
break;
}
}
boo[i].booking_id = acc_rea;
boo[i].book_state +=1;
//put_str("书本信息已更新!",Delay_Time);
printf("预约成功!\n");
_find_book_number(i);
show_people_reader();
return;
}
else if(rea[ord_rea].booking_book_num >=3) //自己预约数大于3
{
put_str("预约图书已达上限,无法继续预约!",Delay_Time);
return;
}
else
{
put_str("该图书暂时不在可预约状态!",Delay_Time);
return;
}
}
}
if(i==Book_cnt)
{
put_str("未检索到该图书,请检查id是否正确!",Delay_Time);
return;
}
return;
}
6.遇到的问题Q及解决办法A
Q:缓冲区问题
A:封装函数,清空缓冲区
Q:多文件编译时无法调用到结构体/函数
A:检查声明所在的行数,在调用前确保编译器能看到声明。同时检查函数调用时是否缺少参数,函数名是否一致等等
Q:fscanf读取格式化输入乱码问题
A:1、整型数组类型无法用fscanf一次性全部读取,解决方法是换fread。2、没有正确申请内存,解决方法是用calloc按格式进行内存分配
Q:函数逻辑运行与预期不符
A:检查符号,检查函数逻辑,打断点检查成功运行的片段,逐步筛查未成功执行语句/片段
Q:编译运行错误,报大量游离中文字符
A:检查标点符号,全部检查依然没解决,删除所有空格,替换为tab