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;

}

  

你可能感兴趣的:(C++)