#define MAX_FN 32 //文件名的最大长度(含后缀)
#define NUM 8 //默认输入或创建数
#define PS 12 //密码的最长长度
#define MAX_NA 20 //姓名最大长度
#define MAX_ID 12 //学号长度
#define MAX_AG 4 //年龄
#define MAX_X 4 //性别
#define CLSBUF() rewind(stdin)
#define POSLEN (strlen(".zhw")) //后缀名
typedef unsigned char UCHAR;
typedef struct
{
char name[MAX_NA]; //姓名
char ID[MAX_ID]; //学号
//char clas[32]; //班级 略
char age[MAX_AG]; //年龄
//float ave[]; //成绩 略
char sex[MAX_X]; //性别
} STU;
struct fhead //记录文件信息的头部
{
char fps[PS]; //打开文件密码
int num; //文件中记录总数或实时记录总数
};
typedef struct
{
struct fhead head; //文件头部
int Gnum; //分配的顺序存储结构的实时大小(以结构体为单位)
int flag; //保存状态
char fn[MAX_FN]; //记录实时打开的文件名
} FLAG;
enum _flag
{
SAV_Y=01, //保存提示
EDT_Y=02, //修改或删除提示
};
void welcome(void); //欢迎界面
void menu(STU **,FLAG *); //菜单调用
void myexit(STU **,FLAG *); //退出
int creatSTU(STU **,unsigned); //分配或扩展内存空间
char *gets_s(char *,int); //字符串输入
int demand(STU **,FLAG *n); //查询记录
void delSTU(STU **,FLAG *); //删除记录
void browse(STU **,FLAG *); //全部浏览(分屏)
void myopen(STU **,FLAG *); //打开文件
int enter(STU **,FLAG *); //输入记录
void edit(STU **,FLAG *); //编辑记录
void myfree(STU **); //释放堆内存
int password(FLAG *n); //设置密码模块
int save(STU **,FLAG *); //保存
void save_as(STU **,FLAG *); //另存为...
int Autoload(STU **,FLAG *); //自动加载项
int filename(char *) ; //文件名处理函数
int issave(STU **,FLAG *); //是否需要保存
void goxy(int, int); //光标定位
void frame(int, int, int, int); //画方框
int main(void)
{
STU *s=NULL; //指向动态分配的空间(顺序存储)
FLAG now= {{{0},0},0,0,{0}};
system("color 9f"); //system("@color XY"); 颜色设置
Autoload(&s,&now);
welcome();
menu(&s,&now);
return 0;
}
static UCHAR zwrebit(UCHAR num) //倒排位函数 用于数据加密
{
UCHAR i,a=0U;
for (i=1U; i!=0U; i<<=1)
{
a<<=1;
if (num & 1) a|=1;
num>>=1;
}
return a;
}
int filename(char *f) //文件名处理函数
{
unsigned n=strlen(f);
if(f==NULL || n==0 || strcmp(f,".zhw")==0)
return 0;
if(strstr(f,".zhw")!=NULL && f[n-1]=='w' && n!=4)
return 1;
if(n < MAX_FN-POSLEN)
memcpy(f+n,".zhw",(POSLEN+1)*sizeof(char));
else
memcpy(f+MAX_FN-POSLEN-1,".zhw",(POSLEN+1)*sizeof(char));
return 1;
}
char *gets_s(char *s,int n) //字符串输入
{
int c;
register char *cs;
if((cs = s)==NULL) return NULL;
CLSBUF();
while(--n>0 && (c=getc(stdin))!=EOF && c!='/n')
*cs++=c;
*cs='/0';
return (c==EOF && cs==s) ||(c=='/n' && cs==s)? NULL : s ;
}
int creatSTU(STU **s,unsigned n)//分配或扩展内存空间
{
STU *temp=(STU *)realloc(*s,sizeof(STU)*n);
if(temp==NULL)
return 0;
*s=temp;
return 1;
}
void myfree(STU **s) //释放堆内存
{
if(*s==NULL)
return;
free(*s);
*s=NULL;
}
static char *passwordENT(char *ps) //密码输入
{
register int j=0;
CLSBUF();
while(j <PS-1 && (ps[j]=getch())!='/r')
{
if(ps[j]=='/033') //ESC键退出
{
*ps='/033';
return ps;
}
if(ps[j]=='/b')
{
if(j>0)
{
printf("/b /b");
j--;
}
}
else
{
putchar('*');
j++;
}
CLSBUF();
}
putchar('*'); //迷惑
ps[j]='/0';
return ps;
}
static int passwordCHK(char *ps) //核对密码
{
if(ps==NULL)
return 0;
char temp[PS]= {0};
int i=3;
while(strcmp(passwordENT(temp),ps)!=0)
{
if(*temp=='/033' || --i<=0)
return 0;
printf("你还可以进行%d次尝试/n请重新输入:",i);
}
return 1;
}
int password(FLAG *n) //设置密码模块
{
if(n==NULL)
return 0;
char temp[PS]= {0};
memcpy(temp,n->head.fps,PS*sizeof(char));//原密码备份
if(*n->head.fps!='/0') //原文件有密码保护
{
printf("请输入原密码,ESC退出:"); //修改前先进行密码核对
if(passwordCHK(n->head.fps)==0)
return 0;
}
printf("/n请输入新密码,ESC退出:");
if(*passwordENT(n->head.fps)=='/033')
{
memcpy(n->head.fps,temp,PS*sizeof(char));//恢复原密码
puts("/n取消修改,任意键退出...");
return 0;
}
n->flag |= EDT_Y;//提示进行了改动
puts("/n修改成功,任意键返回...");
return 1;
}
void myopen(STU **s,FLAG *n) //打开文件
{
char temp[MAX_FN]= {0};
if(issave(s,n)) //检查是否需要保存
{
printf("原文件是否保存:--Y-- 保存 || --ANY KEY-- 不保存/n");
CLSBUF();
if((*temp=getch())=='Y' || *temp=='y')
{
if(save(s,n)==0)
{
fprintf(stderr,"原文件保存出错,不能打开新文件,任意键返回.../n");
getch();
return;
}
}
}
printf("/n请输入要打开的文件:");
if(gets_s(temp,MAX_FN)==NULL || filename(temp)==0)
{
fprintf(stderr,"文件名出错,任意键返回.../n");
getch();
return ;
}
memcpy(n->fn,temp,MAX_FN*sizeof(char));
if(Autoload(s,n)==0) //初始化出错
{
*n->fn='/0';
n->head.num=0;
n->flag=0;
}
}
static int input(STU *s) //输入一个记录
{
if(s==NULL )
return 0;
printf("/n姓名:");
if(gets_s(s->name,sizeof(s->name))==NULL)
{
fprintf(stderr,"姓名输入失败/n");
}
printf("/n学号:");
if(gets_s(s->ID,sizeof(s->ID))==NULL)
{
fprintf(stderr,"学号输入失败/n");
if(*s->name=='/0') //如果姓名也输入错,则返回
return 0;
}
printf("/n年龄:");
if(gets_s(s->age,sizeof(s->age))==NULL)
{
fprintf(stderr,"姓名年龄失败/n");
}
printf("/n性别:");
if(gets_s(s->sex,sizeof(s->sex)-1)==NULL)
{
fprintf(stderr,"性别输入失败/n");
}
return 1;
}
int enter(STU **s,FLAG *n)
{
system("cls");
int i=0; //记录成功输入的个数
do
{
if(n->Gnum==n->head.num+i)
{
n->Gnum+=NUM;
if(creatSTU(s,n->Gnum)==0)
{
fprintf(stderr,"内存分配失败,不能继续输入,注意保存/n");
return 0;
}
}
printf("/n输入第%d个记录:/n",n->head.num +i+1);
if(input(*s + n->head.num+i))
i++;
puts("/n按ESC结束,任意键继续...");
}
while(getch()!='/033');
n->head.num += i;
n->flag |= i<<4; //为以后扩展预留空间
/*将新增的记录数附于flag域中,便于单独存储.且2^28足够存储(32bit)*/
n->flag |= SAV_Y ; //提示需存储
puts("/n输入完毕,注意保存。按任意键回菜单窗口");
return 1;
}
int save(STU **s,FLAG *n) //保存文件
{
if(*s==NULL || n==NULL) return 0;
struct fhead ppt;
UCHAR *temp=(UCHAR *)&n->head;
for(int i=0; i<sizeof(struct fhead); i++)
((UCHAR *)&ppt)[i]=zwrebit(temp[i]);
if(*n->fn=='/0') //无文件则新建
{
char c;
do
{
printf("/n输入文件名:");
if(gets_s(n->fn,MAX_FN)==NULL)
fprintf(stderr,"/n未输入文件名:'Y' 重新输入,任意键返回.../n");
else if(filename(n->fn)==0)
printf("/n文件名出错:'Y' 重新输入,任意键返回.../n");
else break;
CLSBUF();
}
while((c=getch())=='Y' || c=='y');
}
STU ddt[n->head.num]; //加密?
temp=(UCHAR *)*s;
for(int i=0; i<sizeof(STU)*n->head.num; i++)
((UCHAR *)ddt)[i]=zwrebit(temp[i]);
if(filename(n->fn)) //文件名处理
{
FILE *fp=NULL;
if((n->flag & (EDT_Y|SAV_Y)) == SAV_Y) //没有调用过编辑或删除函数但SAV_Y指示仍需保存
{
if((fp=fopen(n->fn,"rb+"))==NULL) //留意打开方式
{
fprintf(stderr,"打开文件出错/n");
return 0;
}
rewind(fp);
if(fwrite(&ppt,sizeof(struct fhead),1,fp)!=1)
{
fprintf(stderr,"写文件头出错/n");
fclose(fp);
return 0;
}
fseek(fp,0L,2);
int j=(unsigned)n->flag>>4; //取出附于flag中的新增数量
for(int i=0; i<j; i++)
{
//fseek(fp,0L,1);
if(fwrite(ddt+n->head.num-j+i,sizeof(STU),1,fp)!=1)
{
printf("写第%d个记录出错/n",n->head.num-j+i+1);
}
}
n->flag &= ~SAV_Y; //提示已保存
}
else if((n->flag & EDT_Y) != 0) //调用了删除或编辑函数
{
if((fp=fopen(n->fn,"wb"))==NULL)
{
fprintf(stderr,"打开文件出错/n");
return 0;
}
if(fwrite(&ppt,sizeof(struct fhead),1,fp)!=1)
{
fprintf(stderr,"写文件头出错/n");
fclose(fp);
return 0;
}
for(int i=0; i< n->head.num; i++)
{
if(fwrite(ddt+i,sizeof(STU),1,fp)!=1)
{
printf("写第%d个记录出错/n",i+1);
}
}
}
if(fp!=NULL)
fclose(fp);
n->flag &= (SAV_Y|EDT_Y); //清除输入数信息并保留操作信息
puts("/n保存完毕。任意键返回...");
return 1;
}
else
{
*n->fn='/0';
return 0;
}
}
void save_as(STU **s,FLAG *n) //另存为...
{
char temp[MAX_FN]= {0};
FILE *fp;
if(*s==NULL)
return ;
printf("/n文件名输入:");
if(gets_s(temp,MAX_FN)!=NULL && filename(temp)==1)
{
if((fp=fopen(temp,"wb"))==NULL)
{
fprintf(stderr,"新建文件出错,任意键返回.../n");
return ;
}
memcpy(n->fn,temp,MAX_FN*sizeof(char)); //更新实时状态,下同
struct fhead ppt;
UCHAR *temp=(UCHAR *)&n->head;
for(int i=0; i<sizeof(struct fhead); i++)
((UCHAR *)&ppt)[i]=zwrebit(temp[i]);
if(fwrite(&ppt,sizeof(struct fhead),1,fp)!=1)
{
fprintf(stderr,"写文件头出错.../n");
return;
}
STU ddt[n->head.num]; //加密?
temp=(UCHAR *)*s;
for(int i=0; i<sizeof(STU)*n->head.num; i++)
((UCHAR *)ddt)[i]=zwrebit(temp[i]);
for(int i=0; i < n->head.num; i++)
if(fwrite(ddt+i,sizeof(STU),1,fp)!=1)
{
fprintf(stderr,"第%d个记录写出错,任意键返回.../n",i);
}
n->flag &= ~SAV_Y ; //提示已保存过
fclose(fp);
puts("另存成功。任意键返回...");
}
else
{
fprintf(stderr,"文件名出错,任意键返回.../n");
}
}
int Autoload(STU **s,FLAG *n)//自动加载 先定义一个STU指针
{
if(s==NULL || n==NULL)
exit(0);
FILE *fp=NULL;
if(*n->fn!='/0') //打开文件
{
if(access(n->fn,0)==-1) //无此文件则新建
{
if((fp=fopen(n->fn,"wb"))==NULL)
{
fprintf(stderr,"新建文件出错,任意键返回.../n");
return 0;
}
n->head.num=0; //
*n->head.fps='/0'; //memset(n->head.ps,0x00,PS*sizeof(char));
if(fwrite(&n->head,sizeof(struct fhead),1,fp)!=1)
{
fprintf(stderr,"写入文件头时出错.../n");
fclose(fp);
unlink(n->fn); // 删除刚建立的文件
*n->fn='/0'; //由于num已清空,故应设为/0
return 0;
}
}
else //打开此文件
{
if((fp=fopen(n->fn,"rb"))==NULL)
{
fprintf(stderr,"打开文件出错,任意键返回.../n");
return 0;
}
struct fhead ppt; //解密?如何封装一下?
fread(&ppt,sizeof(struct fhead),1,fp);
UCHAR *temp=(UCHAR *)&ppt;
for(int i=0; i<sizeof(struct fhead); i++)
temp[i]=zwrebit(temp[i]);
n->head=ppt;
if(*n->head.fps!='/0') //原文件有密码保护
{
printf("请输入文件密码:");
if(passwordCHK(n->head.fps)==0) //密码输入错误
{
fprintf(stderr,"/n密码错误,任意键返回.../n");
fclose(fp);
*n->fn='/0'; //未加载成功,故应设为/0
*n->head.fps='/0';
getch();
return 0;
}
}
}
}
if(n->head.num >= n->Gnum) //扩展存储空间
{
n->Gnum=n->head.num+NUM; //更新实时状态
if(creatSTU(s,n->Gnum)==0)
{
myfree(s);
fprintf(stderr,"/n初始化失败,按任意键退出...");
getch();
exit(0);
}
}
STU ddt[n->head.num];
for(int i=0; i< n->head.num; i++)
{
fread(ddt+i,sizeof(STU),1,fp);
}
for(int i=0; i<sizeof(STU)*n->head.num; i++)
((UCHAR *)*s)[i]=zwrebit(((UCHAR *)ddt)[i]);
if(fp!=NULL) fclose(fp);
n->flag=0;
return 1;
}
static void dir_one(const STU *s ,const int n)//显示一个记录
{
if(s==NULL || n<0)
{
puts("/n未找到相应记录...");
return;
}
puts("/n/t姓名/t性别/t年龄/t学号");
printf("/t%s/t%s/t%s/t%s/n",s[n].name,s[n].sex,s[n].age,s[n].ID);
}
static int search(STU *s,int num)//查找
{
char temp[MAX_NA];
register int i;
printf("/n请输入学生姓名或学号:");
if(gets_s(temp,sizeof(temp))==NULL)
return -1;
for(i=0; i<num; i++)
{
if((strcmp(temp,s[i].name)==0) || (strcmp(temp,s[i].ID)==0))
break;
}
return (i>=num) ? -1: i;
}
int demand(STU **s,FLAG *n)//查询模块
{
system("cls");
if(s==NULL)
puts("/n未能成功加载,任意键返回...");
else if(n->head.num<=0)
puts("/n没有原始记录,任意键返回...");
else
{
int i=search(*s,n->head.num);
dir_one(*s,i);
return i;
}
return -1;
}
void browse(STU **s,FLAG *n)//分屏全部浏览
{
system("cls");
puts("/t-------------------------------");
for(register int i=0; i< n->head.num; i++)
{
if(i!=0 && (i%6)==0)
{
puts("/nESC键退出,任意键继续...");
if(getch()=='/033') break;
}
dir_one(*s,i);
}
puts("/n/t------------------------------");
printf("/t/t/t共有%d个学生记录/n",n->head.num);
puts("/n按任意键回菜单窗口...");
}
static int delSTU_one(STU *s,FLAG *n,int i)//删除一个记录
{
if(i<0 || i>=n->head.num || s==NULL)
{
puts("/n不存在此记录...");
return 0;
}
if(i==n->head.num-1)
{
n->head.num-=1;
return 1;
}
memmove(s+i,s+i+1,sizeof(STU)*(n->head.num-1-i));
n->head.num-=1;
return 1;
}
void delSTU(STU **s,FLAG *n)///删除模块 要修改
{
char c;
if(n->head.num==0)
{
fprintf(stderr,"/n/n数据为空,任意键返回.../n");
return;
}
puts("/n/t 0.删除一个记录");
puts("/t 1.删除全部记录"); //不是删除文件
puts("/t 2.返回菜单");
printf("/n请选择:");
CLSBUF();
if((c=getchar())!='0' && c!='1' && c!='2')
{
fprintf(stderr,"/n输入错误,任意键返回.../n");
return;
}
if(c=='0')
{
if(delSTU_one(*s,n,search(*s,n->head.num))==0)
{
fprintf(stderr,"/n任意键返回.../n");//无此记录
return;
}
}
else if(c=='1')
{
printf("/n确认全删除--Y-- 删除 --ANY KEY-- 不删除:");
/**即使在退出时点"不保存"也无法恢复数据,因为以"wb"方式新建了文件*/
CLSBUF();
if((c=getchar())=='Y' || c=='y')
{
if(*n->fn!='/0')
{
FILE *fp;
if((fp=fopen(n->fn,"wb"))==NULL)
{
fprintf(stderr,"/n全部删除时失败,任意键返回.../n");
return;
}
else
{
n->head.num=0;
if(fwrite(&n->head,sizeof(struct fhead),1,fp)!=1)
{
fprintf(stderr,"/n写入记录总数时失败,任意键返回.../n");
//fclose(fp);
//return;
}
fclose(fp);
}
}
else n->head.num=0; //NO FILES
}
else
{
puts("/n任意键返回...");
return;
}
}
n->flag |= EDT_Y; //提示进行了删除操作
puts(c=='0'?"/n删除成功,任意键回菜单窗口...":"/n任意键返回...");
}
void edit(STU **s,FLAG *n)//编辑记录
{
int i,j=0;
char c;
system("cls");
do
{
if((i=demand(s,n))!=-1) //没找到要编辑的记录
{
puts("/n/t 1. 全部修改...");
puts("/t 2. 修改姓名...");
puts("/t 3. 修改学号...");
puts("/t 4. 修改年龄...");
puts("/t 5. 修改性别...");
while(printf("/n请选择(回车退出):"),CLSBUF(),(c=getchar())!='/n')
{
switch(c)
{
case '1':
input((*s+i));
break;
case '2':
printf("姓名:");
if(gets_s((*s+i)->name,MAX_NA)==NULL)
fprintf(stderr,"姓名输入失败/n");
break;
case '3':
printf("学号:");
if(gets_s((*s+i)->ID,MAX_ID)==NULL)
fprintf(stderr,"学号输入失败/n");
break;
case '4':
printf("年龄:");
if(gets_s((*s+i)->age,MAX_AG)==NULL)
fprintf(stderr,"年龄输入失败/n");
break;
case '5':
printf("性别:");
if(gets_s((*s+i)->sex,MAX_X-1)==NULL)
fprintf(stderr,"性别输入失败/n");
break;
default :
break;
}
}
j++;
}
printf("/n是否继续修改: --Y-- 是 --ANY KEY-- 否");
CLSBUF();
}
while((c=getch())=='Y' || c=='y');
if(j>0)
n->flag |= EDT_Y;
}
static void dir_menu(FLAG *n)
{
system("cls");
goxy(3,3);
puts("/t/t 学生学籍管理系统");
puts("/t/t---------------------------");
puts("/t/t/t功 能 菜 单");
puts("/t/t---------------------------");
printf("/t/t/t 当前文件为:%s/n",n->fn);
puts("/t/t 0. 密码设置 ...");
puts("/t/t 1. 输 入 ...");
puts("/t/t 2. 浏 览 ...");
puts("/t/t 3. 查 询 ...");
puts("/t/t 4. 删 除 ...");
puts("/t/t 5. 保 存 ...");
puts("/t/t 6. 另存为 ...");
puts("/t/t 7. 编 辑 ...");
puts("/t/t 8. 打 开 ...");
puts("/t/t 9. 退 出 ...");
//puts("/t/t---------------------------");
frame(1,1,30,18);
}
void menu(STU **s,FLAG *n)
{
char c;
do
{
dir_menu(n);
printf("/t/t/t/t请选择(0-9):[ ]/b/b");
CLSBUF();
}
while((c=getchar())<'0' || c>'9');
//CLSBUF();
switch(c)
{
case '0':
password(n);
getch();
menu(s,n);
break;
case '1':
enter(s,n);
getch();
menu(s,n);
break;
case '2':
browse(s,n);
getch();
menu(s,n);
break;
case '3':
demand(s,n);
puts("/n任意键返回菜单窗口...");
getch();
menu(s,n);
break;
case '4':
delSTU(s,n);
getch();
menu(s,n);
break;
case '5':
save(s,n);
getch();
menu(s,n);
break;
case '6':
save_as(s,n);
getch();
menu(s,n);
break;
case '7':
edit(s,n);
//getch();
menu(s,n);
break;
case '8':
myopen(s,n);
//getch();
menu(s,n);
break;
case '9':
myexit(s,n);
break;
}
}
void myexit(STU **s,FLAG *n) //退出模块
{
if(issave(s,n))
{
char c;
printf("/n是否保存:--Y-- 保存 || --ANY KEN-- 不保存/n");
CLSBUF();
if((c=getch())=='Y' || c=='y')
save(s,n);
}
myfree(s);
system("cls");
printf("/n/n/n/n/n/n/n/n%30c 伟成工作室竭诚为你服务 %c/n",17,16);
Sleep(3000L);
//exit(0);
}
int issave(STU **s,FLAG *n) //是否需要保存
{
if((n->flag & (SAV_Y | EDT_Y)) !=0) //提示有输入但未存储
return 1;
return 0; //提示不需要保存
}
void welcome(void) //欢迎模块
{
printf("/n/n/n/n/n/n/n/n%30c 欢 迎 使 用 %c/n",03,03);
Sleep(1000L);
system("cls");
printf("/n/n/n/n/n/n/n/n%30c 伟成工作室荣誉出品 %c/n",17,16);
Sleep(1000L);
}
void goxy(int nX, int nY)
{
HANDLE hCon; //定义一个句柄
hCon = GetStdHandle(STD_OUTPUT_HANDLE); //获得输出设备的句柄
COORD setps; //定义结构体变量
setps.X = nX;
setps.Y = nY;
SetConsoleCursorPosition(hCon,setps); //定位
}
void frame(int x, int y, int width, int height)
{
for(int nI = 0; nI < height; nI++) //行数
{
for(int nJ = 0; nJ < width*2; nJ += 2) //列数,width*2中文符号占两位
{
goxy(x + nJ, y + nI);
if(nI == 0 || nJ == width*2 - 2 || nJ == 0 || nI == height - 1)
{
//只打印四个边
if(nI == 0 && nJ == 0 ) //左上的拐角
{
printf("┏");
}
else if(nI == 0 && nJ == width * 2 - 2)
{
printf("┓");//右上的拐角
}
else if(nI == height - 1 && nJ == 0)
{
printf("┗");//左下的拐角
}
else if(nI == height - 1 && nJ == width*2 - 2)
{
printf("┛");//右下的拐角
}
else if(nI == 0 || nI == height - 1)
{
printf("━");
}
else
{
printf("┃");
}
}
}
printf("/n");
}
}