表达式求值(小学生训练系统,加人性化的页面)

#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");///然后清屏
}
/*--------------------------------------------------------------------*/

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值