zoukankan      html  css  js  c++  java
  • C++ 编写计算器 附带自动查错功能(输入表达式输出运算结果)

    好久没写随笔了啊。 这几天都在上课,还有准备今年的区域赛,在整理数据结构模板的时候,把去年大二上学期编的一个程序找了出来,和大家分享下,互相交流。

    当时老师布置的作业,C++编写一个计算器,实现如下功能:

    1.输入形如如同 1+3*5= 的表达式,输出运算结果。 输入包含数字 + -  * / 括号 数学函数

    2.自动查错 若输入表达式不合法(1++3-2),比如 1*2+5-3)= 要提示在第3个位置缺少左括号(当然位置不唯一,位置是从0开始计数)。

    再比如1.5+6/0=  或者  1.5.5+3=  要分别提示除数为0  在第3个位置出现多余小数点。

    3.实现一些数学函数 我这里只选用了3个三角函数sin() cos() tan()

    4.带括号和优先级判断 比如 1+(5-2)-3*9= 先计算(5-2) 在计算3*9  总之是先括号,再乘除,最后加减。

    5.能够灵活处理正负号 比如一些特殊的输入1+sin(-(-(-3.14)))=

    当时自己写了一个程序,帮同班另一女同学也写了一个,感觉不难,思路是肯定有的,只是写代码有点麻烦,搞了一晚上,新鲜的C++计算器出炉了(小程序求大家不要笑)。

    下面说说大体思路吧

    首先读入表达式

    然后把表达式中空格和一些无效字符删除,字符全部变成小写,便于后面比较。

    然后查错 查错分 是否有非法字符,是否括号匹配 ,是否缺少运算符等等。括号匹配这个直接用一个栈来判定即可,其它的讨论下。

    当然要顺便记录每对括号匹配的位置,因为后面要用,有递归实现求值功能。

    然后计算数值,采用递归的写法。比如表达式 3+sin(3.14+2) 先计算位置0到length这个区间的数值,然后递归计算3+位置2到位置length这个区间的数值,这样是相当方便的。

    至于计算数值的话,考虑有括号和+-*/优先级 我采用后缀表达式计算,也就是逆波兰式。 把运算符+-*/映射成很小的实数 当然要保证这些实数不会出现在操作数里面,具体的逆波兰式计算表达式,百度知道,数据结构里面也学了的。

     如果运算中出现很小的负数可能会出问题,因为我符号的hash也映射成负数的,根据情况改一下const double inf和const double eps即可!

    自己验证了许多数据,都能输出正确结果,如果大家有发现BUG的,希望留言,我已经把这个弄成计算表达式的模板了。

    代码

    Calc.h

    #ifndef CALC_H_INCLUDED
    #define CALC_H_INCLUDED
    
    #include<cstdio>
    
    const int MAXN = 200;
    
    class Calc{
    private:
        char Exp[MAXN];			//表达式
        int NextB[MAXN];		//匹配括号位置
        double Ans;				//求值结果
        void DelandLower(char *str);		//删除空字符 转化为小写
        bool Check(char *str,int & len);
        bool CheckCh(const char *str,int pos);	//检查字符
        bool Is_Num(char c);		//是否为数字
        bool Operat(char c);		//是否为运算符
        bool CheckError(const char *str,int len);
        bool CrectB(const char *str);		//检查括号匹配
        bool Equal(double a,double b);		//判断浮点数相等
        int Prio(double x);				//符号优先级判断
        double hash(char c);			//符号到浮点型映射
        double GetV(const char *str,int st,int ed);		//区间求值
    public:
        void Input(){gets(Exp);}
        void Output(){printf("%.2f\n",Ans);}
        bool Cac();
    };
    
    #endif // CALC_H_INCLUDED
    

      

    Calc.cpp

    #include"Calc.h"
    #include<stack>
    #include<cmath>
    #include <cstring>
    #include <iostream>
    
    const double inf = 1e11;
    const double eps = 1e-6; //eps 调整精度
    const int MAXFUN = 3;
    
    #define HASHA (-inf+1)
    #define HASHS (-inf+2)
    #define HASHM (-inf+3)
    #define HASHD (-inf+4)
    #define HASHL (-inf+5)
    #define ERRORX (-inf+6)
    
    using namespace std;
    
    static char MathFun[][4]={"sin","cos","tan"};
    
    double Calc::hash(char c){
        switch(c){
            case '+':return HASHA;
            case '-':return HASHS;
            case '*':return HASHM;
            case '/':return HASHD;
            default :return HASHL;
        }
    }
    
    int Calc::Prio(double x){
        if(x<-inf+3-eps)  //代表加法和减法
            return 1;
        if(x<-inf+5-eps) //乘法和除法
            return 2;
        return 3;
    }
    
    void Calc::DelandLower(char *str){
        int i,j;
        for(i=j=0;*(str+i);i++){
            if(*(str+i)==' ' || *(str+i)=='\t')
                continue;
    		if(*(str+i)>='A' && *(str+i)<='Z')
    			*(str+i)+='a'-'A';
            *(str+j)=*(str+i);
    		j++;
        }
        *(str+j)=0;
    }
    
    bool Calc::Operat(char c){
        switch(c){
            case '+':
            case '-':
            case '*':
            case '/':return 1;
            default :return 0;
        }
    }
    
    bool Calc::Is_Num(char c){
        return c>=48 && c<=57;
    }
    
    bool Calc::CheckCh(const char *str,int pos)
    {
    	int i,j,k; //i扫描到字符串第i个字符,j控制行,k控制列
    
    	for(i=pos;*(str+i);i++){
    		if(Is_Num(*(str+i))
    			|| Operat(*(str+i)) || *(str+i)=='.'
    			|| *(str+i)=='(' || *(str+i)==')')
    			continue;
    		for(j=0;j<MAXFUN;j++){
    			for(k=0;k<MAXFUN;k++){
    				if(*(str+i+k)!=*(*(MathFun+j)+k)) //递归调用MathFun 检查是否匹配数学函数
    					break;
    			}
    			if(k>=3)
    				break;
    		}
    		if(j>=3){
    			printf("在%d位置出现非法字符\n",i);
    			return 0;
    		}
    		else{
    			if(*(str+i+3)!='('){
    				printf("在%d位置缺少左括号\n",i+3);
    				return 0;
    			}
    			return CheckCh(str,i+3);
    		}
    	}
    	return 1;
    }
    
    bool Calc::CrectB(const char *str)
    {
        stack<int> s;
    
    	for(int i=0;*(str+i);i++){
    		if(*(str+i)!='(' && *(str+i)!=')')
    			continue;
    		if(*(str+i)=='('){
    		    s.push(i);
    		}
    		else
    		if(s.empty()){
    			printf("在%d位置出现多余右括号\n",i);
    			return 0;
    		}
    		else{
    			NextB[s.top()]=i;
    			s.pop();
    		}
    	}
    	if(!s.empty()){
            printf("在%d位置出现多余左括号\n",s.top());
    		return 0;
    	}
    	return 1;
    }
    
    bool Calc::CheckError(const char *str,int len){
    	for(int i=0;i<len;i++){
    		if(*(str+i)=='('){
    			if(i<len-1 && Operat(str[i+1]) && str[i+1]!='-'){
    				printf("在%d位置缺少运算符\n",i+1);
    				return 0;
    			}
    			if(i>0 && (Is_Num(str[i-1]) || str[i-1]==')')){
    				printf("在%d位置缺少运算符\n",i);
    				return 0;
    			}
    		}
    		else
    		if(*(str+i)==')'){
    			if(i>0 && (Operat(str[i-1]) || str[i-1]=='(')){
    			    if(Operat(str[i-1]))
                        printf("在%d位置缺少运算符\n",i);
                    else
                        printf("在%d位置缺少数字\n",i);
    				return 0;
    			}
    			if(i<len-1 && Is_Num(str[i+1])){
    				printf("在%d位置缺少运算符\n",i+1);
    				return 0;
    			}
    		}
    		else
    		if(i>0 && Operat(*(str+i)) && Operat(str[i-1])){
    				printf("在%d位置缺少数字\n",i);
    				return 0;
    		}
    	}
    	return 1;
    }
    
    bool Calc::Check(char *str,int & len){
        if(len<(1<<1)){
            puts("表达式长度异常");
            return 0;
        }
        if(str[len-1]!='=' || Operat(str[len-2])){
            puts("表达式结尾错误");
            return 0;
        }
        str[--len]=0;
        if(!CheckCh(str,0) || !CrectB(str) || !CheckError(str,len))
            return 0;
        return 1;
    }
    bool Calc::Equal(double a,double b){
        if(fabs(a-b)<eps)
            return 1;
        return 0;
    }
    
    double Calc::GetV(const char *str,int st,int ed){
        struct P{
            double x,flag;
            bool point;
    		int sign;
    		P(){Init();}
            void Init(){
    			x=0.0;flag=1e-1;
    			sign=1;point=0;
            }
        }Num;
    	stack<double> S;
    	double *Suffix=new double[ed-st+1];
        int sz=0;
    	int i;
        for(i=st;i<ed;i++){
            if(Is_Num(*(str+i)) || *(str+i)=='.')
                if(*(str+i)=='.')
                    if(Num.point==1){
                        printf("在%d位置出现多余小数点\n",i);
                        return ERRORX;
                    }
                    else
                        Num.point=1;
                else
                    if(Num.point==1){
                        Num.x+=Num.flag*(*(str+i)-48);
                        Num.flag*=1e-1;
                    }
                    else
                        Num.x=Num.x*1e1+(*(str+i)-48);
            else{
                if(i>st && Is_Num(str[i-1])){
                    Suffix[sz++]=Num.x*Num.sign;
                    Num.Init();
                }
                if(*(str+i)=='s' || *(str+i)=='c' || *(str+i)=='t'){
                    double ret=0.0;
    				switch(*(str+i)){
    					case 's':ret=sin(GetV(str,i+4,NextB[i+3]));break;
    					case 'c':ret=cos(GetV(str,i+4,NextB[i+3]));break;
    					default :ret=tan(GetV(str,i+4,NextB[i+3]));
    				}
                    if(Equal(ret,ERRORX))
                        return ERRORX;
    				Num.x=ret;
                    Suffix[sz++]=Num.x*Num.sign;
                    Num.Init();
                    i=NextB[i+3];
                }
    			else
                if(*(str+i)==')'){
                    while(!S.empty() && !Equal(HASHL,S.top())){
                        Suffix[sz++]=S.top();
                        S.pop();
                    }
                    S.pop();
                }
                else{
    				char c=*(str+i);
    				if(*(str+i)=='-'){
    					Num.sign=-Num.sign;
    					if(i>st && str[i-1]!='(')
    						c='+';
    					else
    						continue;
    				}
                    while(!S.empty() && !Equal(S.top(),HASHL) && Prio(S.top())>=Prio(hash(c))){
                        Suffix[sz++]=S.top();
                        S.pop();
                    }
                    S.push(hash(c));
                }
            }
        }
    	if(Is_Num(str[ed-1]))
    		Suffix[sz++]=Num.x*Num.sign;
        while(!S.empty()){
            Suffix[sz++]=S.top();
            S.pop();
        }
    	double a,b,cur;
        for(i=0;i<sz;i++){
            cur=Suffix[i];
            if(cur>-inf+10){
                S.push(cur);
            }
            else{
                b=S.top();
                S.pop();
                a=S.top();
                S.pop();
                if(Equal(HASHA,cur))
                    S.push(a+b);
                else
                if(Equal(HASHS,cur))
                    S.push(a-b);
                else
                if(Equal(HASHM,cur))
                    S.push(a*b);
                else
    			{
    				if(Equal(b,0.0))
    				{
    					puts("错误:除数出现0!");
    					return ERRORX;
    				}
                    S.push(a/b);
    			}
            }
        }
    	delete []Suffix;
    	return S.top();
    }
    
    bool Calc::Cac(){
        DelandLower(Exp);
        int len=strlen(Exp);
        if(!Check(Exp,len)) return 0;
        Ans=GetV(Exp,0,len);
        if(Equal(Ans,ERRORX))
            return 0;
        return 1;
    }
    

      

    main.cpp

    #include <iostream>
    #include<stdlib.h>
    #include"Calc.h"
    using namespace std;
    
    int main()
    {
        Calc c;
        cout<<"	 smallCaculator"<<endl;
        cout<<endl;
        cout<<"Don't forget entering '=' at last"<<endl;
        cout<<"for example: 1+5/10-sin(3.1415926/2)="<<endl;
        cout<<"============================"<<endl;
    	while(1){
    		cout<<"Please enter the expression:"<<endl;
            c.Input();
            if(c.Cac()) c.Output();
            system("pause");
            cout<<"============================"<<endl;
    	}
        return 0;
    }
    

      

  • 相关阅读:
    POJ2823 Sliding Window【双端队列】
    初识Identity
    dSploitzANTI渗透教程之启动zANTI工具
    dSploitzANTI渗透教程之安装zANTI工具
    iOS Sprite Kit教程之滚动场景
    iOS Sprite Kit教程之场景的切换
    iOS Sprite Kit教程之场景的设置
    iOS Sprite Kit教程之真机测试以及场景的添加与展示
    iOS Sprite Kit教程之申请和下载证书
    iOS Sprite Kit教程之使用帮助文档以及调试程序
  • 原文地址:https://www.cnblogs.com/lxglbk/p/2716005.html
Copyright © 2011-2022 走看看