2018年数据结构实验之计算器(栈的应用)

2018年数据结构实验之计算器(栈的应用)

数据结构老师问我们“计算器实验和学生信息管理系统实验,你们选一个吧”,好多同学都说,管理系统C语言实训做过了,做计算器吧,加减乘除,好做。
好做????我的老天爷…一会我贴出来,看代码长度就行了,/吐血!
做实验那几周,恶心的要死要死的,有两天熬到了半夜三点还在debug。
栈的操作括号匹配处理负数中缀表达式转后缀表达式计算后缀表达式…第二恶心的是老师要求“不同的错误,程序要给出不同的提示”,于是我的“判断表达式正误”的函数贼长,然后考虑各种各样的错误情况,真的吐血。
第一恶心的是老师要求“实时判错,还能实验退格”。比如这个例子:我的表达式出错了,然后程序会将第一个错误之后的都抹掉,把前面正确的输出出来,允许你对原来的表达式进行改动。即计算机输出1+7272+(8+72之后,我还能用退格键对这串字符进行删除。
实现退格
众所周知,程序输出的字符是不可以删除的,只有自己从键盘输入的字符在按回车之前可以删除,然后为了实现删除程序输出的字符,借鉴室友在网上找到的小技巧,费了很大心机才做出来了…用getch函数实现实时输入、用**\b和\t实现了“删除程序输出字符”,实际上是用识别退格键**、光标前移并且输出空格进行了一个“小魔术”,欺骗了用户的眼睛。后台的数组中存储的还是原来的字符,并没有改变。
还有一个地方是半夜两点突发奇想对一个瓶颈进行了突破,在482-484行。就是如何对后台的数组进行修改,之前想了很多方法,都无济于事,那天晚上我发誓要把它写出来,半夜的代码思维真的挺强的,起码那天是这样,效率很高,灵机一动,三行代码解决问题,心里美美的!
肝了好几个深夜,最后还是做了出来。读者可以自行复制粘贴看使用效果,我个人觉得很完美了,至少从用户使用来看,可能代码上还有很多冗余的地方,望批评指正。
代码中“中缀转后缀”和“计算后缀表达式”参考了大神的博客,忘记出处了,如需要声明来源,请私信,十分感谢!

贴代码

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<conio.h>
#include<windows.h>
#define maxn 100
char s1[maxn];///s1存中缀表达式
char s2[maxn];///s2存后缀表达式
int system(const char*string);///清屏头文件
void setcursor(int y, int x)///光标定位头文件
{
    x--;y--;
    HANDLE hCon = GetStdHandle(STD_OUTPUT_HANDLE);
    COORD setps;
    setps.X = x; setps.Y = y;
    SetConsoleCursorPosition(hCon, setps);
}
void Init()
{
    printf("* * * * * * * * * * * * * * * * * * * * * * * * * * * *\n");
    printf("*                  欢迎使用本计算器                   *\n");
    printf("*    仅支持输入数字,小数点,英文小括号与四则运算符     *\n");
    printf("*                                  exit退出 clear清屏 *\n");
    for(int u=1;u<15;u++)
    printf("*                                                     *\n");
    printf("*                                       作者:王晨1938*\n");
    printf("* * * * * * * * * * * * * * * * * * * * * * * * * * * *\n");
    setcursor(4,3);
}
typedef struct Char_stack
{
     char date[maxn];
     int top;
}cstack;
typedef struct Date_stack
{
    double date[maxn];
    int top;
}dstack;
///初始化字符栈
void Init_cstack(cstack *s)
{
    s->top=-1;
}
///字符栈压栈
bool Push_cstack(cstack *s,char c)
{
    if(s->top<maxn){
        s->top++;
        s->date[s->top]=c;
        return true;
    }
    return false;
}
///字符栈出栈
char Pop_cstack(cstack *s)
{
    char c=s->date[s->top];
    s->top--;
    return c;
}
///取字符栈栈顶元素
char Top_cstack(cstack *s)
{
    return s->date[s->top];
}
///初始化数据栈
void Init_dstack(dstack *s)
{
    s->top=-1;
}
///数据栈压栈
bool Push_dstack(dstack *s,double date)
{
    if(s->top<maxn){
        s->top++;
        s->date[s->top]=date;
        return true;
    }
    return false;
}
///数据栈出栈
double Pop_dstack(dstack *s)
{
    double c=s->date[s->top];
    s->top--;
    return c;
}
///特判单独一个数
bool Spe_Jud(char *s,int t)
{
    int i=0,num=0,flag=0,temp;
    double sum=0,dou=0;
    int sum2=0;
    while(i<t){
        if(s[i]<='9'&&s[i]>='0')
            num++;///对数字计数
        else if(s[i]=='.'){
            flag++;
            temp=i;///temp记录小数点出现的位置
        }
        i++;
    }
    if(num==t){///如果数字个数等于t(全部为数字)的话
        printf("*  计算结果为:");
        for(int k=0;k<=t-1;k++){
            sum=sum*10+s[k]-'0';
        }
        printf("%lf\n",sum);
        return false;
    }
    else if(flag!=0&&num+flag==t){
        for(int p=0;p<=temp-1;p++)
            sum=sum*10+s[p]-'0';

        for(int q=temp+1;q<=t-1;q++)
            dou=dou*10+s[q]-'0';
        for(int j=t-temp-1;j>0;j--)
            dou*=0.1;
        printf("*  计算结果为:");
        printf("%lf",sum+dou);
        printf("\n");
        return false;
    }
    else
        return true;
}
///判断表达式正误
int Judge(char *s,int t)///-1为正确,-2为不正确也不重新输入,其他为不正确但要重新输入
{
    int i=0;
    int flag=0;
    ///判断1.1.1
    for(int ii=0;ii<t;ii++){
        if(flag==0&&s[ii]=='.')
            flag=1;
        else if(flag==1&&s[ii]=='.'){
            printf("*  表达式错误!同一个数字不可以包含多个小数点\n");
            return -2;
        }
        else if(s[ii]=='+'||s[ii]=='-'||s[ii]=='*'||s[ii]=='/'||s[ii]=='('||s[ii]==')')
            flag=0;
    }
    ///判断其他错误
    while(s[i]!='\0'){
        ///开头只能是数字或者左括号或者负号
        if(i==0){
            ///注意判断顺序
            if(s[i]>='0'&&s[i]<='9'){
                if(s[i+1]=='('){
                    printf("*  第%d个字符“%c”错误!“%c”后边不能直接加左括号\n",i+2,s[i+1],s[i]);
                    return i+1;
                }
                else
                    i++;
            }
            else if(s[i]=='('){
                if(s[i+1]=='-'||s[i+1]=='('||s[i+1]<='9'&&s[i+1]>='0'||s[i+1]=='\0')
                    i++;
                else{
                    printf("*  第%d个字符“%c”错误!“%c”后面只能是负号,左括号,数字\n",i+2,s[i+1],s[i]);
                    return i+1;
                }
            }
            else if(s[i]=='-'){
                if(s[i+1]=='('||s[i+1]<='9'&&s[i+1]>='0'||s[i+1]=='\0')
                    i++;
                else{
                    printf("*  第%d个字符“%c”错误!“%c”后面只能是左括号或数字\n",i+2,s[i+1],s[i]);
                    return i+1;
                }
            }
            else{
                printf("*  第%d个字符“%c”错误!表达式只能以数字,左括号,负号开始\n",i+2,s[i+1]);
                return i+1;
            }
        }
        ///结尾
        else if(i==t-1){
            if(s[i]<='9'&&s[i]>='0'||s[i]==')')
                i++;
            else{
                printf("*  表达式错误!表达式只能以数字或右括号结尾\n");
                return -2;
            }
        }
        ///非开头非结尾
        else{
            ///数字后边不能是左括号
            if(s[i]<='9'&&s[i]>='0'){
                if(s[i+1]=='('){
                    printf("*  第%d个字符“%c”错误!“%c”后边不能直接加左括号\n",i+2,s[i+1],s[i]);
                    return i+1;
                }
                else
                    i++;
            }
            ///加减乘后边只能是数字或者左括号
            else if(s[i]=='+'||s[i]=='-'||s[i]=='*'){
                if(s[i+1]=='('||s[i+1]<='9'&&s[i+1]>='0'||s[i+1]=='\0')
                    i++;
                else{
                    printf("*  第%d个字符“%c”错误!“%c”后面只能是左括号或数字\n",i+2,s[i+1],s[i]);
                    return i+1;
                }
            }
            ///除后边只能是除0以外的数字或者左括号
            else if(s[i]=='/'){
                if(s[i+1]=='0'&&s[i+2]!='.'){
                    printf("*  第%d个字符“0”错误!“0”不能作为除数!\n",i+2);
                    return -2;
                }
                else{
                    if(s[i+1]=='('||s[i+1]<='9'&&s[i+1]>'0'||s[i+1]=='\0'||s[i+2]=='.')
                        i++;
                    else{
                        printf("*  第%d个字符“%c”错误!“%c”后面只能是左括号,0以外数字\n",i+2,s[i+1],s[i]);
                        return i+1;
                    }
                }
            }
            ///左括号后边只能是左括号、负号或数字
            else if(s[i]=='('){
                if(s[i+1]=='-'||s[i+1]=='('||s[i+1]<='9'&&s[i+1]>='0'||s[i+1]=='\0')
                    i++;
                else{
                    printf("*  第%d个字符“%c”错误!“%c”后面只能是左括号,负号,数字\n",i+2,s[i+1],s[i]);
                    return i+1;
                }
            }
            ///右括号后边只能是运算符
            else if(s[i]==')'){
                if(s[i+1]=='+'||s[i+1]=='-'||s[i+1]=='*'||s[i+1]=='/'||s[i+1]==')'||s[i+1]=='\0')
                    i++;
                else{
                    printf("*  第%d个字符“%c”错误!“%c”后面只能是运算符或右括号\n",i+2,s[i+1],s[i]);
                    return i+1;
                }
            }
            ///小数点后边只能是数字
            else if(s[i]=='.'){
                if(s[i+1]<='9'&&s[i+1]>='0'||s[i+1]=='\0')
                    i++;
                else{
                    printf("*  第%d个字符“%c”错误!“%c”后只能是数字\n",i+2,s[i+1],s[i]);
                    return i+1;
                }
            }
            else{
                printf("*  第%d个字符“%c”错误!只支持数字、四则运算符、小数点和小括号\n",i+2,s[i+1]);
                return i+1;
            }
        }
    }
    return -1;
}
///判断括号匹配
bool Is_Match(char str[],int n)
{
    int i=0;
    char ch;
    cstack st;
    Init_cstack(&st);
    st.top=0;
    while(i<n){
        if(str[i]=='(')
            Push_cstack(&st,str[i]);
        else{
            if(str[i]==')'){
                Pop_cstack(&st);
                if(st.top<0)
                    return false;
            }
        }
        i++;
    }
    if(st.top==0)
        return true;
    else
        return false;

}
///处理负数问题
void Handle_Negative(char *s)
{
    int i=0,j=0;
    while(s[i]!='\0'){
        if(s[0]=='-'){///如果第一位就是符号
            j=strlen(s);
            while(j>0){
                s[j+5]=s[j];
                j--;
            }
            s[j++]='(';
            s[j++]='0';
            s[j++]='-';
            s[j++]='1';
            s[j++]=')';
            s[j]='*';
        }
        if(s[i]=='('&&s[i+1]=='-'){
            j=strlen(s);
            while(j>i+1){
                s[j+5]=s[j];
                j--;
            }
            s[j++]='(';
            s[j+1]='0';
            s[j++]='-';
            s[j++]='1';
            s[j++]=')';
            s[j]='*';
            i=i+5;
        }
        i++;
    }
}
///当前扫描运算符优先级
int Isop(char str)
{
    if(str=='(')
        return 6;
    else if(str=='+'||str=='-')
        return 2;
    else if(str=='*'||str=='/')
        return 4;
}
///当前扫描运算符优先级
int Inop(char str)
{
    if(str=='(')
        return 1;
    else if(str=='+'||str=='-')
        return 3;
    else if(str=='*'||str=='/')
        return 5;
}
///中缀表达式转后缀表达式
void Change(char *s1,char *s2)
{
    int i=0,j=0;
    int flag1=-1,flag2=-1;
    cstack str;
    Init_cstack(&str);
    while(s1[i]!='\0')
    {
        if(flag1==0&&flag2==1)///若上次的输出为数字,上次循环扫描为字符,则表示该数字串结束,则在数字后加空格区分
        {
            s2[j++]=' ';
            flag1=1;
        }
        if(s1[i]>='0'&&s1[i]<='9'||s1[i]=='.')
        {
            s2[j++]=s1[i];
            flag2=0;
            flag1=0;
        }
        else if(s1[i]=='+'||s1[i]=='-'||s1[i]=='*'||s1[i]=='/'||s1[i]=='(')
        {
            flag2=1;
            if(str.top<0||Isop(s1[i])>Inop(Top_cstack(&str)))
            {
                Push_cstack(&str,s1[i]);
            }
            else
            {
                ///当前扫描字符优先级不断与栈顶字符优先级比较,当前字符小于栈顶字符时退栈并输出
                while(str.top>=0&&Isop(s1[i])<Inop(Top_cstack(&str)))
                {
                    s2[j++]=Pop_cstack(&str);
                    flag1=1;
                }
                ///当前字符优先级大于栈顶优先级或栈空时当前字符压入字符栈内
                if(str.top<0||Isop(s1[i])>Inop(Top_cstack(&str)))
                {
                    Push_cstack(&str,s1[i]);
                }
            }
        }
        else if(s1[i]==')')
        {

            flag2=1;
            if(Top_cstack(&str)!='(')	///若括号仅包含数字则没有输出运算符
            {
                flag1=1;
            }
            while(Top_cstack(&str)!='(')
            {
                s2[j++]=Pop_cstack(&str);
            }
            Pop_cstack(&str);///将(出栈
        }
        i++;
    }
	while(str.top>=0)///将栈内剩余的运算符依次退栈输出
    {
        s2[j++]=Pop_cstack(&str);
    }
	s2[j]='\0';
}
///计算后缀表达式
double Calcu(char *s1)
{
    int i=0;
	int flag;				///char类型转换为double类型数据标记
	double data1,data2;
	double sum;
	dstack ds1;
	Init_dstack(&ds1);
	while(s1[i]!='\0')
    {
        ///若为运算符获取栈顶两个元素进行计算
        if(s1[i]=='+'||s1[i]=='-'||s1[i]=='*'||s1[i]=='/')
        {
            data1=Pop_dstack(&ds1);
            data2=Pop_dstack(&ds1);
            if(s1[i]=='+') Push_dstack(&ds1,data2+data1);
            else if(s1[i]=='-') Push_dstack(&ds1,data2-data1);
            else if(s1[i]=='*') Push_dstack(&ds1,data2*data1);
            else if(s1[i]=='/') Push_dstack(&ds1,data2/data1);
        }
        ///若为数据时压栈
        else
        {
            flag=0;
            sum=0;
            double divider=1;
            while(s1[i]!=' '&&s1[i]!='+'&&s1[i]!='-'&&s1[i]!='*'&&s1[i]!='/')
            {
                if(s1[i]=='\0'){
                    break;
                }
                if(s1[i]=='.')
                {
                    flag=1;
                    i++;
                    continue;
                }
                if(flag==0)
                {
                    sum=sum*10+(double)(s1[i]-'0');
                }
                else
                {
                    divider=divider*10;
                    sum=sum+((double)(s1[i]-'0'))/divider;
                }
                i++;
            }
            if(s1[i]=='+'||s1[i]=='-'||s1[i]=='*'||s1[i]=='/')
                i--;
            else if(s1[i]=='\0')
                break;
            Push_dstack(&ds1,sum);
        }
        i++;
    }
	return Pop_dstack(&ds1);
}
///错误表达式退格修改
void Reflash(char *s1,int i)
{
    printf("*  请重新输入正确的表达式\n");
    printf("*  ");
    for(int j=0;j<i;j++)
        printf("%c",s1[j]);
    char c;
    int k=0;
    int m;
    while(1){
        c=getch();
        if(c=='\b'){
            printf("\b \b");
            k++;
        }
        else if(c=='\r'){
            break;
        }
        else{
            printf("%c",c);
            m=k;
            s1[i-m]=c;
            k--;
        }
    }///我真是天才,这里太巧妙了
    s1[i-k]='\0';
    printf("\n");
}
///递归判断重新输入的表达式的正误并输出正确结果
void Recursion(char *s1)
{
    int t=strlen(s1);
    if(!Is_Match(s1,t)){
        printf("*  表达式括号不匹配!\n");
    }
    else{
        int r=Judge(s1,t);
        if(r==-1){
            if(Spe_Jud(s1,t)){
                Handle_Negative(s1);
                Change(s1,s2);
                printf("*  计算结果为:%lf\n",Calcu(s2));
            }
        }
        else if(r==-2){
            //continue;
        }
        else{
            Reflash(s1,r);
            Recursion(s1);
        }
    }
}
int main()
{
    Init();
    int i=1;
    while(1){
        if(i==1){
            printf(" ");
            printf("请输入正确的表达式:\n");
        }
        else
            printf("*  请输入正确的表达式:\n");
        i++;
        printf("*  ");
        scanf("%s",s1);
        if(s1[0]>='a'&&s1[0]<='z'||s1[0]>='A'&&s1[0]<='Z'){
            if(s1[0]=='c'&&s1[1]=='l'&&s1[2]=='e'&&s1[3]=='a'&&s1[4]=='r'){
                system("cls");
                Init();
            }
            else if(s1[0]=='e'&&s1[1]=='x'&&s1[2]=='i'&&s1[3]=='t'){
                printf("*  计算器运行结束,欢迎您再次使用!\n");
                break;
            }
            else{
                printf("*  只支持数字、四则运算符、小数点和小括号\n*  退出请输入exit,清屏请输入clear\n");
            }

        }
        else
            Recursion(s1);
    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值