#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<ctime>
#include<stack>
#include<conio.h>
using namespace std;
///13个子函数,4个文件
const int maxn=99;///这个定义的题库的数量最大数
const int maxm=20;///这个定义是账号和密码的最大长度
char account_user[maxm],password_user[maxm];///这个放在全局变量,便于传递,这个是用户的信息
struct node///表达式结构体
{
char s[99];///这个是表达式的长度
int answer;///这个是表达式的答案,不一定是正确的,因为是用户输入进来的答案
}Tiku[maxn];///题库的最大数量
/*--------------------各种相关登陆函数--------------------------------*/
bool login_manager();///管理员登陆函数
bool login_user();///用户登陆函数
/*--------------------------------------------------------------------*/
/*--------------------浏览函数----------------------------------------*/
void browse_manager();///管理员浏览用户的历史成绩函数
void browse_user();///用户浏览自己的历史成绩函数
/*--------------------------------------------------------------------*/
/*--------------------注册函数----------------------------------------*/
bool registe_user();///注册新用户函数
/*--------------------------------------------------------------------*/
/*--------------------创建题库函数------------------------------------*/
void creat();///管理员创建题库函数
bool check(char *);///检查管理员创建的题库是否合法的函数
/*--------------------------------------------------------------------*/
/*--------------------保存用户做题信息函数----------------------------*/
void save_score(int );///用户训练用把他的成绩录入到文件里的函数
/*--------------------------------------------------------------------*/
/*--------------------核心部分,计算过程------------------------------*/
void exercise();///训练函数
bool result(char *,double &);///运算的结果函数
void calculate(double ,double ,char ,double &);///两个数出栈的时候运算的函数
char precede(char a,char b);///比较两个操作符的优先级的函数
bool Is_oper(char );///判断字符是否为+-*/()的函数
/*--------------------------------------------------------------------*/
/*--------------------清屏函数----------------------------------------*/
void clear_print();///清屏函数
/*--------------------------------------------------------------------*/
int main()
{
int op,opt,choose,cmd1,cmd2;///opt是选择管理员模式还是用户模式还是注册
system("color 0A");///界面的背景函数
menu:
printf("\t\t\t*******************\n");
printf("\t\t\t* 1.管理员界面 *\n");
printf("\t\t\t* 2.用户界面 *\n");
printf("\t\t\t* 3.注册 *\n");
printf("\t\t\t* 0.退出 *\n");
printf("\t\t\t*******************\n");
printf("\t\t\t请选择你要进入的页面:");
while(1)
{
scanf("%d",&opt);
if(opt==0)
{
printf("\t\t\t欢迎下次再使用!\n");
return 0;
}
fflush(stdin);
if(opt==1||opt==2||opt==3)///必须是选择正确的,不正确的话提示再次输入
break;
else
printf("输入错误,请重新输入:");
}
if(opt==1)///管理员模式
{
system("cls");///清屏之前的页面
do
{
printf("\t\t\t******管理员界面******\n");
printf("\t\t\t**********************\n");
printf("\t\t\t* 1.登陆 *\n");
printf("\t\t\t* 0.退出 *\n");
printf("\t\t\t**********************\n");
printf("\t\t\t请输入需要操作的菜单:");
scanf("%d",&cmd1);
fflush(stdin);
if(cmd1==0)
{
printf("\t\t\t欢迎下次使用!\n");///如果不进行登陆,也就说明结束了
return 0;
}
else if(cmd1==1)
{
if(login_manager())///登陆函数,如果输入不正确的话,提示再次输入
{
printf("\t\t\t登陆成功!\n");
clear_print();///清屏
break;
}
else
{
printf("\t\t\t账号不存在或密码错误,请重新输入!\n");
}
}
else
printf("\t\t\t输入错误,请重新输入!\n");
clear_print();///清屏
}while(1);///登陆成功之后才能进行下面的内容
do{
printf("\t\t\t********************\n");
printf("\t\t\t*1.创建题库 *\n");
printf("\t\t\t*2.查看用户历史成绩*\n");
printf("\t\t\t*0.退出 *\n");
printf("\t\t\t********************\n");
printf("\t\t\t请输入需要操作的菜单:");
scanf("%d",&choose);
fflush(stdin);
if(choose==0)
{
printf("\t\t\t欢迎下次再使用!\n");
break;
}
switch(choose)
{
case 1:creat();break;///创建题库
case 2:browse_manager();break;///浏览用户的历史成绩
default:printf("输入错误,请重新输入!\n");break;
}
clear_print();///清屏
}while(1);
}
else if(opt==2)///用户模式
{
system("cls");///清屏之前的内容
do
{
printf("\t\t\t*****用户界面*****\n");
printf("\t\t\t******************\n");
printf("\t\t\t* 1.登陆 *\n");
printf("\t\t\t* 0.退出 *\n");
printf("\t\t\t******************\n");
printf("\t\t\t请输入需要操作的菜单:");
scanf("%d",&cmd2);
fflush(stdin);
if(cmd2==0)///不登陆也就是结束了
{
printf("\t\t\t欢迎下次使用!\n");
return 0;
}
else if(cmd2==1)
{
if(login_user())///登陆不成功提示再次输入
{
printf("\t\t\t登陆成功!\n");
clear_print();///清屏
break;
}
else
{
printf("\t\t\t账号不存在或密码错误,请重新输入!\n");
}
}
else
printf("\t\t\t输入错误,请重新输入!\n");
clear_print();///清屏
}while(1);///登陆成功之后才能进入下面的操作
do
{
printf("\t\t\t**********************\n");
printf("\t\t\t* 1.训练 *\n");
printf("\t\t\t* 2.查看历史成绩 *\n");
printf("\t\t\t* 0.退出 *\n");
printf("\t\t\t**********************\n");
printf("\t\t\t请输入需要操作的菜单:");
scanf("%d",&choose);
fflush(stdin);
if(choose==0)
{
printf("\t\t\t欢迎下次再使用!\n");
return 0;
}
switch(choose)
{
case 1:exercise();break;///训练函数
case 2:browse_user();break;///浏览历史成绩函数
default:printf("输入错误,请重新输入!\n");break;
}
clear_print();///清屏
}while(1);
}
else if(opt==3)
{
if(registe_user())///注册成功,提示用户
{
printf("注册成功!\n");
clear_print();
goto menu;
}
else///注册失败并提示原因
{
printf("注册失败!(账号已存在或账号可能含非法字符或密码长度小于6位或");
printf("密码过于简单(密码应同时包含字母和数字))!\n");
while(1)
{
printf("\t\t\t是否继续?继续请按1,退出请按0:");
scanf("%d",&op);
if(op==1)
{
clear_print();
goto menu;
break;
}
else if(op==0)
return 0;
else
printf("\t\t\t输入错误,请重新输入!\n");
}
}
}
return 0;
}
/*--------------------各种相关登陆函数--------------------------------*/
bool login_manager()///管理员登陆函数
{
char account[maxm],account_temp[maxm];///temp是打开文件暂存的变量
char password[maxm],password_temp[maxm];
int ok=0;///标记是否匹配成功
FILE *fp;
fp=fopen("D:\\manager.txt","r");///打开管理员的信息文件
printf("\t\t\t账号:");
scanf("%s",account);
printf("\t\t\t密码:");
int i=0;///i是记录密码的长度
char ch;///ch是暂时的输入
while((ch=getch())!='\r')///这个循环是实现让密码以*的形式呈现出来,安全性方面考虑的,用getch读入就不会显示出来,输入到回车键也就是结束了
{
if(i<maxm&&isprint(ch))///如果输入的是可打印的,也就是正常的,那么久可以保存到数组里
{
password[i++]=ch;///保存到数组里
putchar('*');///并以*的形式显示出来,代表输入的长度
}
else if(i>0&&ch=='\b')///如果检索到的是删除,那么就要进行删除操作
{
--i;///数组自然要自减1
putchar('\b');///光标回退一格
putchar(' ');///这个回退一位的还是以*显示,所以我们改变让它以空格的形式显示
putchar('\b');///我们必须让光标再回退一位,因为这个位置是还不存在的,所以要显示到删除的上一位
}
}
putchar('\n');///显示下一行
password[i]='\0';///结束之后给password加个结束符
fflush(stdin);///清除缓存
while(~fscanf(fp,"%s%s",account_temp,password_temp))///打开文件,进行遍历
{
if((!strcmp(account_temp,account))&&(!strcmp(password_temp,password)))///账号和密码都匹配就说明是正确的
{
ok=1;///匹配成功就标记ok为1,并跳出
break;
}
}
fclose(fp);///关闭文件
if(ok)///匹配成功返回true
return true;
return false;///否则就是匹配失败
}
bool login_user()///用户登陆函数
{
char ch,account_temp[maxm];///ch是暂时的输入
char password_temp[maxm];///temp是打开文件暂存的变量
int ok=0,i=0;///i是记录密码的长度,ok是标记是否匹配成功
FILE *fp;
fp=fopen("D:\\user.txt","r");///打开用户的文件
printf("\t\t\t账号:");
scanf("%s",account_user);
printf("\t\t\t密码:");
while((ch=getch())!='\r')///这个循环是实现让密码以*的形式呈现出来,安全性方面考虑的,用getch读入就不会显示出来,输入到回车键也就是结束了
{
if(i<maxm&&isprint(ch))///如果输入的是可打印的,也就是正常的,那么久可以保存到数组里
{
password_user[i++]=ch;///保存到数组里
putchar('*');///并以*的形式显示出来,代表输入的长度
}
else if(i>0&&ch=='\b')///如果检索到的是删除,那么就要进行删除操作
{
--i;///数组自然要自减1
putchar('\b');///光标回退一格
putchar(' ');///这个回退一位的还是以*显示,所以我们改变让它以空格的形式显示
putchar('\b');///我们必须让光标再回退一位,因为这个位置是还不存在的,所以要显示到删除的上一位
}
}
putchar('\n');///显示下一行
password_user[i]='\0';///结束之后给password_user加个结束符
fflush(stdin);///清除缓存
while(~fscanf(fp,"%s%s",account_temp,password_temp))///打开文件,进行遍历
{
if((!strcmp(account_temp,account_user))&&(!strcmp(password_temp,password_user)))
{
ok=1;///账号和密码都匹配就说明是正确的
break;
}
}
fclose(fp);///关闭文件
if(ok)///匹配成功返回true
return true;
return false;///否则就是匹配失败
}
/*--------------------------------------------------------------------*/
/*--------------------浏览函数----------------------------------------*/
void browse_manager()///管理员浏览用户的历史成绩的函数
{
FILE *fp;
char account[maxm],account_temp[maxm];///有以temp结尾的都是接下来文件使用的暂存变量
int score,grade[maxn],i=0,flag=0;///grade数组的记录用户的成绩
double Time,time[maxn];///time是用户做题时间
fp=fopen("D:\\score.txt","r");///打开成绩的文件
printf("请输入你需要查询的用户的账号:");///输入要查询的账号的历史成绩
scanf("%s",account);
while(~fscanf(fp,"%s %d %lf",account_temp,&score,&Time))
{
if(!strcmp(account,account_temp))///进行匹配,是该查找的账号就存入到相应的数组里
{
flag=1;///有的话就说明存在这个账号,并跳出
break;
}
}
fclose(fp);///关闭文件
if(flag==0)///如果查找一遍都没有该账号,说明就不存在直接返回,不需要再进行接下来的事
{
printf("无此用户,无法进行浏览历史成绩!\n");
return ;
}
fp=fopen("D:\\score.txt","r");///打开成绩的文件
while(~fscanf(fp,"%s %d %lf",account_temp,&score,&Time))
{
if(!strcmp(account,account_temp))///进行匹配,是该查找的账号就存入到相应的数组里
grade[i]=score,time[i]=Time,++i;
}
fclose(fp);///关闭文件
if(i==0)///如果都没查找到,就说明
printf("%s用户还未进行任何练习,尚未存在历史成绩!\n",account);
else
{
printf("账号:%s的历史成绩:\n",account);
for(int j=0;j<i;j++)
printf("第%d次成绩:%d分 用时:%.3f秒\n",j+1,grade[j],time[j]);
}
}
void browse_user()///用户浏览自己的历史成绩
{
FILE *fp;
fp=fopen("D:\\score.txt","r");///打开成绩文件
char account[maxm];///这个是打开文件的暂存变量
int i=0,grade,score[maxn];///记录成绩的数组
double Time,time[maxn];///记录做题时间的数组
int cnt_hundred=0,cnt_ninety=0,cnt_eighty=0,cnt_sixty=0,cnt_zero=0;///这个是各个成绩阶段的次数100 90 80 70-60 60以下
while(~fscanf(fp,"%s %d %lf",account,&grade,&Time))
{
if(!strcmp(account,account_user))///匹配到就记录他相应的分数和用时
score[i]=grade,time[i]=Time,++i;
}
if(i==0)///如果遍历过去都没有,就说明该账号还没有进行练习
{
printf("您还未进行练习,现在开始练习吧,加油!\n");
return ;///没有的话就直接进行返回
}
for(int j=0;j<i;j++)///输出成绩和用时
{
printf("第%d次的成绩:%d分 用时:%.3f秒\n",j+1,score[j],time[j]);
if(score[j]==100)///记录各个阶段的分数段,用于接下来的评价
++cnt_hundred;
else if(score[j]>=90)
++cnt_ninety;
else if(score[j]>=80)
++cnt_eighty;
else if(score[j]>=60)
++cnt_sixty;
else
++cnt_zero;
}
if(i==1)///如果只有一次,那就直接针对这个成绩进行评价
{
if(score[0]==100)
printf("优秀,继续保持!\n");
else if(score[0]>=90)
printf("很不错啊,有更好的提高空间!\n");
else if(score[0]>=80)
printf("良好,要多多练习哦!\n");
else if(score[0]>=60)
printf("看起来很糟糕呢,要多多练习哦!\n");
else
printf("很不幸,成绩是不及格哦,要多次练习哦!\n");
}
else if(i>1)///如果多次的话,就进行各种综合评价
{
if(cnt_hundred!=0&&cnt_ninety!=0&&cnt_eighty==0&&cnt_sixty==0&&cnt_zero==0)
printf("近来表现很不错哦,成绩都保持90分以上哦!\n");
else if(cnt_hundred==0&&cnt_ninety==0&&cnt_eighty!=0&&cnt_sixty==0&&cnt_zero==0)
printf("还不错呢,都保持在90分到80分之间,只要继续努力一定可以提高的哦!\n");
else if(cnt_hundred!=0&&cnt_ninety==0&&cnt_eighty==0&&cnt_sixty!=0&&cnt_zero==0)
printf("很糟糕呢,近来练习的成绩不是很理想,要勤加练习哦!\n");
else if(cnt_hundred!=0&&cnt_ninety!=0&&cnt_eighty==0&&cnt_sixty!=0&&cnt_zero!=0)
printf("成绩不是很好,而且也不稳定,要多练习去提高并让自己的成绩稳定下来!\n");
}
}
/*--------------------------------------------------------------------*/
/*--------------------注册函数----------------------------------------*/
bool registe_user()///注册函数
{
char ch,account[maxm],password[maxm];///ch是暂时的输入
char account_temp[maxm],password_temp[maxm];///temp是打开文件暂存的变量
int k=0;///k是记录密码的长度
int cnt_sign=0,cnt_num=0;///sign是记录非数字的数量,num是记录数字的数量
printf("\t\t\t账号(长度不超过20位):");
scanf("%s",account);
printf("\t\t\t密码(长度不超过20位):");
scanf("%s",password);
for(int i=0;i<strlen(password);i++)
if(password[i]>='0'&&password[i]<='9')
++cnt_num;///是数字自增1
else
++cnt_sign;///是非数字自增1
if(strlen(password)<6||strlen(password)>maxm)///如果密码长度小于6位或者大于20位是错误的,这样可以保证用户的安全性
return false;///返回错误
else
{
if(cnt_num==0||cnt_sign==0)///如果没有同时含有非数字和数字,这个密码也是不安全的,所以也是错误的,提示出来
return false;///返回错误
else
{
FILE *fp;///打开user的文件
fp=fopen("D:\\user.txt","a+");
while(~fscanf(fp,"%s %s",account_temp,password_temp))///进行遍历,看新注册的用户名是否有重复,有重复也要提示错误
if(!strcmp(account,account_temp))
{
fclose(fp);///查找到就关闭文件
return false;///返回错误
}
fclose(fp);///如果查找过去都没有,那就说明不是重复的,可以进行储存了
fp=fopen("D:\\user.txt","a+");///再次打开文件,然后进行录入
fprintf(fp,"%s %s\n",account,password);
fclose(fp);///关闭文件
return true;///返回正确
}
}
}
/*--------------------------------------------------------------------*/
/*--------------------创建题库函数------------------------------------*/
void creat()///创建题库的函数
{
int n,mark,fin;///创建的数量
double ans;
char str[maxn];///表达式的数组
FILE *fp;
fp=fopen("D:\\tiku.txt","a+");
printf("请输入你创建的题库数量:");
scanf("%d",&n);
getchar();///吸回车键,因为下面的输入gets会读入回车键
printf("(长度不超过99位)表达式的样例如(a+b)*c=\n");
for(int i=0;i<n;i++)
{
mark=0;///mark是等下判断时候要用的
printf("第%d题:",i+1);
gets(str);
int j=0;
for(int k=0;str[k]!='\0';k++)///把字符的空格去除掉的循环
if(str[k]!=' ')
Tiku[i].s[j++]=str[k];
Tiku[i].s[j]='\0';
for(int k=0;Tiku[i].s[k]!='\0';k++)
{
if(Tiku[i].s[k]=='/')///如果除号后面有0说明分母是0,也就是不合法的,跳出,并提示
{
if(Tiku[i].s[k+1]=='0')
{
mark=1;
break;
}
}
else if(Tiku[i].s[k]=='.')///如果有小数点的也是错误的,不合法的,跳出
{
mark=1;
break;
}
}
if(mark)///如果除号后面有0说明分母是0,也就是不合法的,跳出,并提示
{
printf("创建题目中分母有为0的或存在小数的数字,请重新输入一个!\n");
--i;
continue;
}
if(!check(Tiku[i].s)||strlen(Tiku[i].s)>maxn)
{
printf("括号不匹配或长度超出,重新输入一个!\n");
--i;
continue;
}
if(!result(Tiku[i].s,ans))///如果这个计算过程中有负数的就返回0,提示错误,并停止接下来的操作
{
printf("计算过程有负数或者是结果为负数,整个运算必须是正数运算结果也是正的,请重新输入一个!\n");
--i;
continue;
}
fin=ans;
if(ans==fin)///fin是整型,如果没有小数的话,结果浮点和整型是可以相等,也就是正确的,读入文件
{
fprintf(fp,"%s\n",Tiku[i].s);
}
else///否则提示错误
{
printf("计算结果存在小数,请记住结果必须为整数的,请重新输入一个!\n");
--i;
}
}
printf("题库列表:\n");///输出刚才创建的题目,给创建者看有没有问题
for(int i=0;i<n;i++)///遍历输出
{
printf("第%d题:",i+1);
printf("%s\n",Tiku[i].s);
}
printf("创建成功!\n");
fclose(fp);///关闭文件
}
bool check(char *s)///检查创建题目的正确性的函数
{
int cnt=0;
for(int i=0;s[i]!='\0';i++)///左右括号匹配的,那么最终的cnt计数肯定是0
if(s[i]=='(')
++cnt;
else if(s[i]==')')
--cnt;
if(cnt==0)///计数为0的话就返回正确
return true;
return false;///否则返回错误
}
/*--------------------------------------------------------------------*/
/*--------------------保存用户做题信息函数----------------------------*/
void save_score(int score,double Time)///保存做题情况
{
FILE *fp;
fp=fopen("D:\\score.txt","a+");///存在这个score.txt的文件里
fprintf(fp,"%s %d %.3f\n",account_user,score,Time);///这个文件有用户名和分数以及做题用时
fclose(fp);///关闭文件
}
/*--------------------------------------------------------------------*/
/*--------------------核心部分,计算过程------------------------------*/
void exercise()///训练函数
{
FILE *fp;
int k=0,key[maxn];///key这个数组是用题目的顺序,用随机生成,达到题目每次生成的都不一样
int score=0;///记录最终成绩
double ans;
fp=fopen("D:\\tiku.txt","r");
srand(time(0));///随机种子
///把所有题库文件的所有题目都读出来
while(~fscanf(fp,"%s",Tiku[k].s)) ++k;///k是题目的数量
fclose(fp);///读出完之后关闭文件
key[0]=rand()%k;///模k,也就是在这个最大题目数量里随机
for(int i=1;i<k;i++)
{
while(1)///用一个死循环,必须是与前一个不同的数字才能跳出
{
int temp=rand()%k;///temp就是下一个,这个必须与上一个不同
int j;
for(j=0;j<i;j++)///跟前面已生成的进行判断,有相同的就跳出,也就是这个随机数不行,必须再换一个
{
if(key[j]==temp)
break;
}
if(j==i)///只有遍历过去都不同的话才是正确的,也就可以跳出循环
{
key[i]=temp;
break;
}
}
}
clock_t start=clock();///这个是记录做题的开始时间
printf("随机的10个练习题(除不尽的保留3位小数):\n");
for(int i=0;i<10;i++)
{
printf("第%d个练习题:",i+1);
printf("%s",Tiku[key[i]].s);
scanf("%d",&Tiku[key[i]].answer);///写一个答案进行匹配
result(Tiku[key[i]].s,ans);
if(Tiku[key[i]].answer==ans)///如果是相同的也就是正确
{
score+=10;///正确加10分
printf("答案正确!\n");
}
else
{
printf("答案错误!\n");///错误的话,告知正确答案
printf("正确答案为:%.f\n",ans);
}
}
clock_t tail=clock();///做题结束的时间
printf("******得分情况******\n");
printf("最终成绩:%d分\n",score);///输出成绩
double Time=(double)(tail-start)/CLOCKS_PER_SEC;///前后相减的时间就是做题时间
printf("用时:%.3f秒\n",Time);///输出做题时间
if(score==100)///对最终成绩的各种等级评价
printf("做的完全无可挑剔了,加油,继续努力保持!\n");
else if(score>=90)
printf("错了一题,不要灰心,下次仔细点一定能全对的!\n");
else if(score>=80)
printf("干得不错,继续努力!\n");
else if(score>=60)
printf("有点不够好哦,要继续练习提升自己!\n");
else
printf("看起来做的不够好哦,要勤加练习呢!\n");
save_score(score,Time);///训练完之后就进行保存文件
}
bool result(char *s,double &ans)///运算的结果函数
{
char op;///op是比较两个操作符优先级返回的字符
double a,b,num;///a,b是操作数,num是提取字符种的数字
int i=0;///point是用来记录小数点的位数
stack<char > oper_stk;///操作符栈
stack<double > num_stk;///数字栈
int j;
oper_stk.push('=');///先入栈一个'=',它的优先级最大,那么一开始操作就可以入栈了。
///而且到最后匹配到=的操作符是就需要它,然后一直出栈,结果也就出来了
while(s[i]!='='||oper_stk.top()!='=')///只要表达式数组不是=,以及操作符栈里的栈顶不是=,就说明操作还没有结束
{
if(s[i]>='0'&&s[i]<='9')///是数字就开始运算,知道遇到操作符,这个数才能被确定下来
{
num=0;///初始化num
num=s[i]-'0';///-'0',也就是把字符型转为整型的操作
j=i+1;///j的赋值的意思就是这个字符操作完看下一个进行操作
while(!Is_oper(s[j]))///来一个循环,如果不是操作符的话,意思这个数还没有被确定下来
{
num=num*10+s[j++]-'0';
}
i=j;///i是建立在找到一个完整的数的位置继续开始
num_stk.push(num);///然后把这个完整的数进行数字栈的入栈
}
else if(Is_oper(s[i]))///如果是操作符的,就是开始比较优先级,要么入栈,要么出栈,要么就是进行运算
{
switch(precede(s[i],oper_stk.top()))///这个函数是进行比较优先级,返回的是一个字符,根据字符来进行操作下一步
{
case '<':oper_stk.push(s[i++]);break;///小于号,说明栈顶的优先级小于当前的,就入栈,因为越后面入栈的优先级越大
case '=':oper_stk.pop();++i;break;///这个是括号匹配,肯定要出栈的
case '>':///当前的操作符小于栈顶,就开始操作
op=oper_stk.top();oper_stk.pop();///在操作栈出栈一个操作符,进行运算
b=num_stk.top();num_stk.pop();///出栈两个数,第一个出栈的一定是第二个数,这个一定要注意,不能颠倒
a=num_stk.top();num_stk.pop();///这个出栈的是第一个数
double fin;
calculate(a,b,op,fin);///然后进行运算,把运算的结果再次入栈到数字栈里,异常操作,知道结束
if(fin<0)///如果运算的时候出现负数就说明是错误的,进行返回错误
return false;
num_stk.push(fin);
break;
}
}
}
ans=num_stk.top();///这时候数字栈的栈顶就是最终的答案
return true;///没有操作错误的话就返回正确
}
void calculate(double a,double b,char op,double &fin)///两个数出栈的时候运算的函数
{
///op是运算符,ab是操作数,fin是运算的结果
if(op=='+')
fin=a+b;
else if(op=='-')
fin=a-b;
else if(op=='*')
fin=a*b;
else if(op=='/')
fin=a/b;
}
char precede(char a,char b)///比较两个操作符的优先级的函数
{
int i,j;
///这个二维数组非常关键,就是靠这个来进行比较优先级
///第一行是当前操作符的位置,第一列是操作栈的栈顶的操作符的位置
char table[8][8]={
{' ','+','-','*','/','(',')','='},
{'+','>','>','<','<','<','>','>'},
{'-','>','>','<','<','<','>','>'},
{'*','>','>','>','>','<','>','>'},
{'/','>','>','>','>','<','>','>'},
{'(','<','<','<','<','<','=',' '},
{')','>','>','>','>',' ','>','>'},
{'=','<','<','<','<','<',' ','='}
};
for(i=0;i<8;i++)
if(table[0][i]==a)///找当前操作符的位置
break;
for(j=0;j<8;j++)
if(table[j][0]==b)///找栈顶的操作符的位置
break;
return table[j][i];///返回栈顶的操作符与当前操作符的优先级
}
bool Is_oper(char s)///判断字符是否为+-*/()的函数
{
char str[8]={'+','-','*','/','(',')','='};///进行辨别,辨别该字符是不是+-*/()
for(int i=0;i<7;i++)
if(str[i]==s)///如果是的话就返回正确
return true;
return false;///遍历过去都没有就说明是数字,返回错误
}
/*--------------------------------------------------------------------*/
/*--------------------清屏函数----------------------------------------*/
void clear_print()///清屏函数
{
printf("按任意键继续......");
getch();///让界面暂停下来
system("cls");///然后清屏
}
/*--------------------------------------------------------------------*/
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<ctime>
#include<stack>
#include<conio.h>
using namespace std;
///13个子函数,4个文件
const int maxn=99;///这个定义的题库的数量最大数
const int maxm=20;///这个定义是账号和密码的最大长度
char account_user[maxm],password_user[maxm];///这个放在全局变量,便于传递,这个是用户的信息
struct node///表达式结构体
{
char s[99];///这个是表达式的长度
int answer;///这个是表达式的答案,不一定是正确的,因为是用户输入进来的答案
}Tiku[maxn];///题库的最大数量
/*--------------------各种相关登陆函数--------------------------------*/
bool login_manager();///管理员登陆函数
bool login_user();///用户登陆函数
/*--------------------------------------------------------------------*/
/*--------------------浏览函数----------------------------------------*/
void browse_manager();///管理员浏览用户的历史成绩函数
void browse_user();///用户浏览自己的历史成绩函数
/*--------------------------------------------------------------------*/
/*--------------------注册函数----------------------------------------*/
bool registe_user();///注册新用户函数
/*--------------------------------------------------------------------*/
/*--------------------创建题库函数------------------------------------*/
void creat();///管理员创建题库函数
bool check(char *);///检查管理员创建的题库是否合法的函数
/*--------------------------------------------------------------------*/
/*--------------------保存用户做题信息函数----------------------------*/
void save_score(int );///用户训练用把他的成绩录入到文件里的函数
/*--------------------------------------------------------------------*/
/*--------------------核心部分,计算过程------------------------------*/
void exercise();///训练函数
bool result(char *,double &);///运算的结果函数
void calculate(double ,double ,char ,double &);///两个数出栈的时候运算的函数
char precede(char a,char b);///比较两个操作符的优先级的函数
bool Is_oper(char );///判断字符是否为+-*/()的函数
/*--------------------------------------------------------------------*/
/*--------------------清屏函数----------------------------------------*/
void clear_print();///清屏函数
/*--------------------------------------------------------------------*/
int main()
{
int op,opt,choose,cmd1,cmd2;///opt是选择管理员模式还是用户模式还是注册
system("color 0A");///界面的背景函数
menu:
printf("\t\t\t*******************\n");
printf("\t\t\t* 1.管理员界面 *\n");
printf("\t\t\t* 2.用户界面 *\n");
printf("\t\t\t* 3.注册 *\n");
printf("\t\t\t* 0.退出 *\n");
printf("\t\t\t*******************\n");
printf("\t\t\t请选择你要进入的页面:");
while(1)
{
scanf("%d",&opt);
if(opt==0)
{
printf("\t\t\t欢迎下次再使用!\n");
return 0;
}
fflush(stdin);
if(opt==1||opt==2||opt==3)///必须是选择正确的,不正确的话提示再次输入
break;
else
printf("输入错误,请重新输入:");
}
if(opt==1)///管理员模式
{
system("cls");///清屏之前的页面
do
{
printf("\t\t\t******管理员界面******\n");
printf("\t\t\t**********************\n");
printf("\t\t\t* 1.登陆 *\n");
printf("\t\t\t* 0.退出 *\n");
printf("\t\t\t**********************\n");
printf("\t\t\t请输入需要操作的菜单:");
scanf("%d",&cmd1);
fflush(stdin);
if(cmd1==0)
{
printf("\t\t\t欢迎下次使用!\n");///如果不进行登陆,也就说明结束了
return 0;
}
else if(cmd1==1)
{
if(login_manager())///登陆函数,如果输入不正确的话,提示再次输入
{
printf("\t\t\t登陆成功!\n");
clear_print();///清屏
break;
}
else
{
printf("\t\t\t账号不存在或密码错误,请重新输入!\n");
}
}
else
printf("\t\t\t输入错误,请重新输入!\n");
clear_print();///清屏
}while(1);///登陆成功之后才能进行下面的内容
do{
printf("\t\t\t********************\n");
printf("\t\t\t*1.创建题库 *\n");
printf("\t\t\t*2.查看用户历史成绩*\n");
printf("\t\t\t*0.退出 *\n");
printf("\t\t\t********************\n");
printf("\t\t\t请输入需要操作的菜单:");
scanf("%d",&choose);
fflush(stdin);
if(choose==0)
{
printf("\t\t\t欢迎下次再使用!\n");
break;
}
switch(choose)
{
case 1:creat();break;///创建题库
case 2:browse_manager();break;///浏览用户的历史成绩
default:printf("输入错误,请重新输入!\n");break;
}
clear_print();///清屏
}while(1);
}
else if(opt==2)///用户模式
{
system("cls");///清屏之前的内容
do
{
printf("\t\t\t*****用户界面*****\n");
printf("\t\t\t******************\n");
printf("\t\t\t* 1.登陆 *\n");
printf("\t\t\t* 0.退出 *\n");
printf("\t\t\t******************\n");
printf("\t\t\t请输入需要操作的菜单:");
scanf("%d",&cmd2);
fflush(stdin);
if(cmd2==0)///不登陆也就是结束了
{
printf("\t\t\t欢迎下次使用!\n");
return 0;
}
else if(cmd2==1)
{
if(login_user())///登陆不成功提示再次输入
{
printf("\t\t\t登陆成功!\n");
clear_print();///清屏
break;
}
else
{
printf("\t\t\t账号不存在或密码错误,请重新输入!\n");
}
}
else
printf("\t\t\t输入错误,请重新输入!\n");
clear_print();///清屏
}while(1);///登陆成功之后才能进入下面的操作
do
{
printf("\t\t\t**********************\n");
printf("\t\t\t* 1.训练 *\n");
printf("\t\t\t* 2.查看历史成绩 *\n");
printf("\t\t\t* 0.退出 *\n");
printf("\t\t\t**********************\n");
printf("\t\t\t请输入需要操作的菜单:");
scanf("%d",&choose);
fflush(stdin);
if(choose==0)
{
printf("\t\t\t欢迎下次再使用!\n");
return 0;
}
switch(choose)
{
case 1:exercise();break;///训练函数
case 2:browse_user();break;///浏览历史成绩函数
default:printf("输入错误,请重新输入!\n");break;
}
clear_print();///清屏
}while(1);
}
else if(opt==3)
{
if(registe_user())///注册成功,提示用户
{
printf("注册成功!\n");
clear_print();
goto menu;
}
else///注册失败并提示原因
{
printf("注册失败!(账号已存在或账号可能含非法字符或密码长度小于6位或");
printf("密码过于简单(密码应同时包含字母和数字))!\n");
while(1)
{
printf("\t\t\t是否继续?继续请按1,退出请按0:");
scanf("%d",&op);
if(op==1)
{
clear_print();
goto menu;
break;
}
else if(op==0)
return 0;
else
printf("\t\t\t输入错误,请重新输入!\n");
}
}
}
return 0;
}
/*--------------------各种相关登陆函数--------------------------------*/
bool login_manager()///管理员登陆函数
{
char account[maxm],account_temp[maxm];///temp是打开文件暂存的变量
char password[maxm],password_temp[maxm];
int ok=0;///标记是否匹配成功
FILE *fp;
fp=fopen("D:\\manager.txt","r");///打开管理员的信息文件
printf("\t\t\t账号:");
scanf("%s",account);
printf("\t\t\t密码:");
int i=0;///i是记录密码的长度
char ch;///ch是暂时的输入
while((ch=getch())!='\r')///这个循环是实现让密码以*的形式呈现出来,安全性方面考虑的,用getch读入就不会显示出来,输入到回车键也就是结束了
{
if(i<maxm&&isprint(ch))///如果输入的是可打印的,也就是正常的,那么久可以保存到数组里
{
password[i++]=ch;///保存到数组里
putchar('*');///并以*的形式显示出来,代表输入的长度
}
else if(i>0&&ch=='\b')///如果检索到的是删除,那么就要进行删除操作
{
--i;///数组自然要自减1
putchar('\b');///光标回退一格
putchar(' ');///这个回退一位的还是以*显示,所以我们改变让它以空格的形式显示
putchar('\b');///我们必须让光标再回退一位,因为这个位置是还不存在的,所以要显示到删除的上一位
}
}
putchar('\n');///显示下一行
password[i]='\0';///结束之后给password加个结束符
fflush(stdin);///清除缓存
while(~fscanf(fp,"%s%s",account_temp,password_temp))///打开文件,进行遍历
{
if((!strcmp(account_temp,account))&&(!strcmp(password_temp,password)))///账号和密码都匹配就说明是正确的
{
ok=1;///匹配成功就标记ok为1,并跳出
break;
}
}
fclose(fp);///关闭文件
if(ok)///匹配成功返回true
return true;
return false;///否则就是匹配失败
}
bool login_user()///用户登陆函数
{
char ch,account_temp[maxm];///ch是暂时的输入
char password_temp[maxm];///temp是打开文件暂存的变量
int ok=0,i=0;///i是记录密码的长度,ok是标记是否匹配成功
FILE *fp;
fp=fopen("D:\\user.txt","r");///打开用户的文件
printf("\t\t\t账号:");
scanf("%s",account_user);
printf("\t\t\t密码:");
while((ch=getch())!='\r')///这个循环是实现让密码以*的形式呈现出来,安全性方面考虑的,用getch读入就不会显示出来,输入到回车键也就是结束了
{
if(i<maxm&&isprint(ch))///如果输入的是可打印的,也就是正常的,那么久可以保存到数组里
{
password_user[i++]=ch;///保存到数组里
putchar('*');///并以*的形式显示出来,代表输入的长度
}
else if(i>0&&ch=='\b')///如果检索到的是删除,那么就要进行删除操作
{
--i;///数组自然要自减1
putchar('\b');///光标回退一格
putchar(' ');///这个回退一位的还是以*显示,所以我们改变让它以空格的形式显示
putchar('\b');///我们必须让光标再回退一位,因为这个位置是还不存在的,所以要显示到删除的上一位
}
}
putchar('\n');///显示下一行
password_user[i]='\0';///结束之后给password_user加个结束符
fflush(stdin);///清除缓存
while(~fscanf(fp,"%s%s",account_temp,password_temp))///打开文件,进行遍历
{
if((!strcmp(account_temp,account_user))&&(!strcmp(password_temp,password_user)))
{
ok=1;///账号和密码都匹配就说明是正确的
break;
}
}
fclose(fp);///关闭文件
if(ok)///匹配成功返回true
return true;
return false;///否则就是匹配失败
}
/*--------------------------------------------------------------------*/
/*--------------------浏览函数----------------------------------------*/
void browse_manager()///管理员浏览用户的历史成绩的函数
{
FILE *fp;
char account[maxm],account_temp[maxm];///有以temp结尾的都是接下来文件使用的暂存变量
int score,grade[maxn],i=0,flag=0;///grade数组的记录用户的成绩
double Time,time[maxn];///time是用户做题时间
fp=fopen("D:\\score.txt","r");///打开成绩的文件
printf("请输入你需要查询的用户的账号:");///输入要查询的账号的历史成绩
scanf("%s",account);
while(~fscanf(fp,"%s %d %lf",account_temp,&score,&Time))
{
if(!strcmp(account,account_temp))///进行匹配,是该查找的账号就存入到相应的数组里
{
flag=1;///有的话就说明存在这个账号,并跳出
break;
}
}
fclose(fp);///关闭文件
if(flag==0)///如果查找一遍都没有该账号,说明就不存在直接返回,不需要再进行接下来的事
{
printf("无此用户,无法进行浏览历史成绩!\n");
return ;
}
fp=fopen("D:\\score.txt","r");///打开成绩的文件
while(~fscanf(fp,"%s %d %lf",account_temp,&score,&Time))
{
if(!strcmp(account,account_temp))///进行匹配,是该查找的账号就存入到相应的数组里
grade[i]=score,time[i]=Time,++i;
}
fclose(fp);///关闭文件
if(i==0)///如果都没查找到,就说明
printf("%s用户还未进行任何练习,尚未存在历史成绩!\n",account);
else
{
printf("账号:%s的历史成绩:\n",account);
for(int j=0;j<i;j++)
printf("第%d次成绩:%d分 用时:%.3f秒\n",j+1,grade[j],time[j]);
}
}
void browse_user()///用户浏览自己的历史成绩
{
FILE *fp;
fp=fopen("D:\\score.txt","r");///打开成绩文件
char account[maxm];///这个是打开文件的暂存变量
int i=0,grade,score[maxn];///记录成绩的数组
double Time,time[maxn];///记录做题时间的数组
int cnt_hundred=0,cnt_ninety=0,cnt_eighty=0,cnt_sixty=0,cnt_zero=0;///这个是各个成绩阶段的次数100 90 80 70-60 60以下
while(~fscanf(fp,"%s %d %lf",account,&grade,&Time))
{
if(!strcmp(account,account_user))///匹配到就记录他相应的分数和用时
score[i]=grade,time[i]=Time,++i;
}
if(i==0)///如果遍历过去都没有,就说明该账号还没有进行练习
{
printf("您还未进行练习,现在开始练习吧,加油!\n");
return ;///没有的话就直接进行返回
}
for(int j=0;j<i;j++)///输出成绩和用时
{
printf("第%d次的成绩:%d分 用时:%.3f秒\n",j+1,score[j],time[j]);
if(score[j]==100)///记录各个阶段的分数段,用于接下来的评价
++cnt_hundred;
else if(score[j]>=90)
++cnt_ninety;
else if(score[j]>=80)
++cnt_eighty;
else if(score[j]>=60)
++cnt_sixty;
else
++cnt_zero;
}
if(i==1)///如果只有一次,那就直接针对这个成绩进行评价
{
if(score[0]==100)
printf("优秀,继续保持!\n");
else if(score[0]>=90)
printf("很不错啊,有更好的提高空间!\n");
else if(score[0]>=80)
printf("良好,要多多练习哦!\n");
else if(score[0]>=60)
printf("看起来很糟糕呢,要多多练习哦!\n");
else
printf("很不幸,成绩是不及格哦,要多次练习哦!\n");
}
else if(i>1)///如果多次的话,就进行各种综合评价
{
if(cnt_hundred!=0&&cnt_ninety!=0&&cnt_eighty==0&&cnt_sixty==0&&cnt_zero==0)
printf("近来表现很不错哦,成绩都保持90分以上哦!\n");
else if(cnt_hundred==0&&cnt_ninety==0&&cnt_eighty!=0&&cnt_sixty==0&&cnt_zero==0)
printf("还不错呢,都保持在90分到80分之间,只要继续努力一定可以提高的哦!\n");
else if(cnt_hundred!=0&&cnt_ninety==0&&cnt_eighty==0&&cnt_sixty!=0&&cnt_zero==0)
printf("很糟糕呢,近来练习的成绩不是很理想,要勤加练习哦!\n");
else if(cnt_hundred!=0&&cnt_ninety!=0&&cnt_eighty==0&&cnt_sixty!=0&&cnt_zero!=0)
printf("成绩不是很好,而且也不稳定,要多练习去提高并让自己的成绩稳定下来!\n");
}
}
/*--------------------------------------------------------------------*/
/*--------------------注册函数----------------------------------------*/
bool registe_user()///注册函数
{
char ch,account[maxm],password[maxm];///ch是暂时的输入
char account_temp[maxm],password_temp[maxm];///temp是打开文件暂存的变量
int k=0;///k是记录密码的长度
int cnt_sign=0,cnt_num=0;///sign是记录非数字的数量,num是记录数字的数量
printf("\t\t\t账号(长度不超过20位):");
scanf("%s",account);
printf("\t\t\t密码(长度不超过20位):");
scanf("%s",password);
for(int i=0;i<strlen(password);i++)
if(password[i]>='0'&&password[i]<='9')
++cnt_num;///是数字自增1
else
++cnt_sign;///是非数字自增1
if(strlen(password)<6||strlen(password)>maxm)///如果密码长度小于6位或者大于20位是错误的,这样可以保证用户的安全性
return false;///返回错误
else
{
if(cnt_num==0||cnt_sign==0)///如果没有同时含有非数字和数字,这个密码也是不安全的,所以也是错误的,提示出来
return false;///返回错误
else
{
FILE *fp;///打开user的文件
fp=fopen("D:\\user.txt","a+");
while(~fscanf(fp,"%s %s",account_temp,password_temp))///进行遍历,看新注册的用户名是否有重复,有重复也要提示错误
if(!strcmp(account,account_temp))
{
fclose(fp);///查找到就关闭文件
return false;///返回错误
}
fclose(fp);///如果查找过去都没有,那就说明不是重复的,可以进行储存了
fp=fopen("D:\\user.txt","a+");///再次打开文件,然后进行录入
fprintf(fp,"%s %s\n",account,password);
fclose(fp);///关闭文件
return true;///返回正确
}
}
}
/*--------------------------------------------------------------------*/
/*--------------------创建题库函数------------------------------------*/
void creat()///创建题库的函数
{
int n,mark,fin;///创建的数量
double ans;
char str[maxn];///表达式的数组
FILE *fp;
fp=fopen("D:\\tiku.txt","a+");
printf("请输入你创建的题库数量:");
scanf("%d",&n);
getchar();///吸回车键,因为下面的输入gets会读入回车键
printf("(长度不超过99位)表达式的样例如(a+b)*c=\n");
for(int i=0;i<n;i++)
{
mark=0;///mark是等下判断时候要用的
printf("第%d题:",i+1);
gets(str);
int j=0;
for(int k=0;str[k]!='\0';k++)///把字符的空格去除掉的循环
if(str[k]!=' ')
Tiku[i].s[j++]=str[k];
Tiku[i].s[j]='\0';
for(int k=0;Tiku[i].s[k]!='\0';k++)
{
if(Tiku[i].s[k]=='/')///如果除号后面有0说明分母是0,也就是不合法的,跳出,并提示
{
if(Tiku[i].s[k+1]=='0')
{
mark=1;
break;
}
}
else if(Tiku[i].s[k]=='.')///如果有小数点的也是错误的,不合法的,跳出
{
mark=1;
break;
}
}
if(mark)///如果除号后面有0说明分母是0,也就是不合法的,跳出,并提示
{
printf("创建题目中分母有为0的或存在小数的数字,请重新输入一个!\n");
--i;
continue;
}
if(!check(Tiku[i].s)||strlen(Tiku[i].s)>maxn)
{
printf("括号不匹配或长度超出,重新输入一个!\n");
--i;
continue;
}
if(!result(Tiku[i].s,ans))///如果这个计算过程中有负数的就返回0,提示错误,并停止接下来的操作
{
printf("计算过程有负数或者是结果为负数,整个运算必须是正数运算结果也是正的,请重新输入一个!\n");
--i;
continue;
}
fin=ans;
if(ans==fin)///fin是整型,如果没有小数的话,结果浮点和整型是可以相等,也就是正确的,读入文件
{
fprintf(fp,"%s\n",Tiku[i].s);
}
else///否则提示错误
{
printf("计算结果存在小数,请记住结果必须为整数的,请重新输入一个!\n");
--i;
}
}
printf("题库列表:\n");///输出刚才创建的题目,给创建者看有没有问题
for(int i=0;i<n;i++)///遍历输出
{
printf("第%d题:",i+1);
printf("%s\n",Tiku[i].s);
}
printf("创建成功!\n");
fclose(fp);///关闭文件
}
bool check(char *s)///检查创建题目的正确性的函数
{
int cnt=0;
for(int i=0;s[i]!='\0';i++)///左右括号匹配的,那么最终的cnt计数肯定是0
if(s[i]=='(')
++cnt;
else if(s[i]==')')
--cnt;
if(cnt==0)///计数为0的话就返回正确
return true;
return false;///否则返回错误
}
/*--------------------------------------------------------------------*/
/*--------------------保存用户做题信息函数----------------------------*/
void save_score(int score,double Time)///保存做题情况
{
FILE *fp;
fp=fopen("D:\\score.txt","a+");///存在这个score.txt的文件里
fprintf(fp,"%s %d %.3f\n",account_user,score,Time);///这个文件有用户名和分数以及做题用时
fclose(fp);///关闭文件
}
/*--------------------------------------------------------------------*/
/*--------------------核心部分,计算过程------------------------------*/
void exercise()///训练函数
{
FILE *fp;
int k=0,key[maxn];///key这个数组是用题目的顺序,用随机生成,达到题目每次生成的都不一样
int score=0;///记录最终成绩
double ans;
fp=fopen("D:\\tiku.txt","r");
srand(time(0));///随机种子
///把所有题库文件的所有题目都读出来
while(~fscanf(fp,"%s",Tiku[k].s)) ++k;///k是题目的数量
fclose(fp);///读出完之后关闭文件
key[0]=rand()%k;///模k,也就是在这个最大题目数量里随机
for(int i=1;i<k;i++)
{
while(1)///用一个死循环,必须是与前一个不同的数字才能跳出
{
int temp=rand()%k;///temp就是下一个,这个必须与上一个不同
int j;
for(j=0;j<i;j++)///跟前面已生成的进行判断,有相同的就跳出,也就是这个随机数不行,必须再换一个
{
if(key[j]==temp)
break;
}
if(j==i)///只有遍历过去都不同的话才是正确的,也就可以跳出循环
{
key[i]=temp;
break;
}
}
}
clock_t start=clock();///这个是记录做题的开始时间
printf("随机的10个练习题(除不尽的保留3位小数):\n");
for(int i=0;i<10;i++)
{
printf("第%d个练习题:",i+1);
printf("%s",Tiku[key[i]].s);
scanf("%d",&Tiku[key[i]].answer);///写一个答案进行匹配
result(Tiku[key[i]].s,ans);
if(Tiku[key[i]].answer==ans)///如果是相同的也就是正确
{
score+=10;///正确加10分
printf("答案正确!\n");
}
else
{
printf("答案错误!\n");///错误的话,告知正确答案
printf("正确答案为:%.f\n",ans);
}
}
clock_t tail=clock();///做题结束的时间
printf("******得分情况******\n");
printf("最终成绩:%d分\n",score);///输出成绩
double Time=(double)(tail-start)/CLOCKS_PER_SEC;///前后相减的时间就是做题时间
printf("用时:%.3f秒\n",Time);///输出做题时间
if(score==100)///对最终成绩的各种等级评价
printf("做的完全无可挑剔了,加油,继续努力保持!\n");
else if(score>=90)
printf("错了一题,不要灰心,下次仔细点一定能全对的!\n");
else if(score>=80)
printf("干得不错,继续努力!\n");
else if(score>=60)
printf("有点不够好哦,要继续练习提升自己!\n");
else
printf("看起来做的不够好哦,要勤加练习呢!\n");
save_score(score,Time);///训练完之后就进行保存文件
}
bool result(char *s,double &ans)///运算的结果函数
{
char op;///op是比较两个操作符优先级返回的字符
double a,b,num;///a,b是操作数,num是提取字符种的数字
int i=0;///point是用来记录小数点的位数
stack<char > oper_stk;///操作符栈
stack<double > num_stk;///数字栈
int j;
oper_stk.push('=');///先入栈一个'=',它的优先级最大,那么一开始操作就可以入栈了。
///而且到最后匹配到=的操作符是就需要它,然后一直出栈,结果也就出来了
while(s[i]!='='||oper_stk.top()!='=')///只要表达式数组不是=,以及操作符栈里的栈顶不是=,就说明操作还没有结束
{
if(s[i]>='0'&&s[i]<='9')///是数字就开始运算,知道遇到操作符,这个数才能被确定下来
{
num=0;///初始化num
num=s[i]-'0';///-'0',也就是把字符型转为整型的操作
j=i+1;///j的赋值的意思就是这个字符操作完看下一个进行操作
while(!Is_oper(s[j]))///来一个循环,如果不是操作符的话,意思这个数还没有被确定下来
{
num=num*10+s[j++]-'0';
}
i=j;///i是建立在找到一个完整的数的位置继续开始
num_stk.push(num);///然后把这个完整的数进行数字栈的入栈
}
else if(Is_oper(s[i]))///如果是操作符的,就是开始比较优先级,要么入栈,要么出栈,要么就是进行运算
{
switch(precede(s[i],oper_stk.top()))///这个函数是进行比较优先级,返回的是一个字符,根据字符来进行操作下一步
{
case '<':oper_stk.push(s[i++]);break;///小于号,说明栈顶的优先级小于当前的,就入栈,因为越后面入栈的优先级越大
case '=':oper_stk.pop();++i;break;///这个是括号匹配,肯定要出栈的
case '>':///当前的操作符小于栈顶,就开始操作
op=oper_stk.top();oper_stk.pop();///在操作栈出栈一个操作符,进行运算
b=num_stk.top();num_stk.pop();///出栈两个数,第一个出栈的一定是第二个数,这个一定要注意,不能颠倒
a=num_stk.top();num_stk.pop();///这个出栈的是第一个数
double fin;
calculate(a,b,op,fin);///然后进行运算,把运算的结果再次入栈到数字栈里,异常操作,知道结束
if(fin<0)///如果运算的时候出现负数就说明是错误的,进行返回错误
return false;
num_stk.push(fin);
break;
}
}
}
ans=num_stk.top();///这时候数字栈的栈顶就是最终的答案
return true;///没有操作错误的话就返回正确
}
void calculate(double a,double b,char op,double &fin)///两个数出栈的时候运算的函数
{
///op是运算符,ab是操作数,fin是运算的结果
if(op=='+')
fin=a+b;
else if(op=='-')
fin=a-b;
else if(op=='*')
fin=a*b;
else if(op=='/')
fin=a/b;
}
char precede(char a,char b)///比较两个操作符的优先级的函数
{
int i,j;
///这个二维数组非常关键,就是靠这个来进行比较优先级
///第一行是当前操作符的位置,第一列是操作栈的栈顶的操作符的位置
char table[8][8]={
{' ','+','-','*','/','(',')','='},
{'+','>','>','<','<','<','>','>'},
{'-','>','>','<','<','<','>','>'},
{'*','>','>','>','>','<','>','>'},
{'/','>','>','>','>','<','>','>'},
{'(','<','<','<','<','<','=',' '},
{')','>','>','>','>',' ','>','>'},
{'=','<','<','<','<','<',' ','='}
};
for(i=0;i<8;i++)
if(table[0][i]==a)///找当前操作符的位置
break;
for(j=0;j<8;j++)
if(table[j][0]==b)///找栈顶的操作符的位置
break;
return table[j][i];///返回栈顶的操作符与当前操作符的优先级
}
bool Is_oper(char s)///判断字符是否为+-*/()的函数
{
char str[8]={'+','-','*','/','(',')','='};///进行辨别,辨别该字符是不是+-*/()
for(int i=0;i<7;i++)
if(str[i]==s)///如果是的话就返回正确
return true;
return false;///遍历过去都没有就说明是数字,返回错误
}
/*--------------------------------------------------------------------*/
/*--------------------清屏函数----------------------------------------*/
void clear_print()///清屏函数
{
printf("按任意键继续......");
getch();///让界面暂停下来
system("cls");///然后清屏
}
/*--------------------------------------------------------------------*/