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;
}

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM