C++ 编写计算器 附带自动查错功能(输入表达式输出运算结果)
2012-10-09 00:39
525 查看
好久没写随笔了啊。 这几天都在上课,还有准备今年的区域赛,在整理数据结构模板的时候,把去年大二上学期编的一个程序找了出来,和大家分享下,互相交流。
当时老师布置的作业,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
Calc.cpp
main.cpp
当时老师布置的作业,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; }
相关文章推荐
- loner_li 机试题 编写一个简单的计算器实现加减乘除:输入两个数字和一个运算符号,输出结果(注意容错性)
- 通过键盘输入100 以内正整数的加、减运算式,请编写一个程序输出运算结果字符串
- 华为:通过键盘输入100以内正整数的加、减运算式,请编写一个程序输出运算结果字符串
- 华为:通过键盘输入100以内正整数的加、减运算式,请编写一个程序输出运算结果字符串
- 机试题:过键盘输入100以内正整数的加、减运算式,请编写一个程序输出运算结果字符串。
- (华为机考题)通过键盘输入100以内正整数的加、减运算式,请编写一个程序输出运算结果字符串。
- C++实现计算器功能(包括计算含未知量的式子),输出后缀表达式
- 华为机试通过键盘输入100以内正整数的加、减运算式,请编写一个程序输出运算结果字符串
- 请编写函数fun(char *s),其功能是:删除字符串中的数字字符。主函数中输入字符串,调用函数,并输出结果字符串。例如若输入的字符串是:34AB9C6DE, 则应输出:ABCDE
- 通过键盘输入100以内正整数的加、减运算式,请编写一个程序输出运算结果字符串。
- 通过键盘输入100以内正整数的加、减运算式,请编写一个程序输出运算结果字符串。 输入字符串的格式为:“操作数1 运算符 操作数2”,“操作数”与“运算符”之间以一个空格隔开。 补充说明: 1、操作数为
- 通过键盘输入100以内正整数的加、减运算式,请编写一个程序输出运算结果字符串。
- 规定输入的字符串中,只能包含字母和*号,编写函数fun(char *a),其功能是:将字符串前、后的连续*号全部删除。主函数中输入字符串,调用函数,并输出结果字符串。例如:若字符串中的内容为: ***
- 华为机试——通过键盘输入100以内正整数的加、减运算式,请编写一个程序输出运算结果字符串
- 从易到难编写C++程序,(1)问题:把键盘输入的字符串逆序输出。
- 第九周实验报告 任务2 定义Complex类中的<<和>>运算符的重载,实现输入和输出,改造原程序中对运算结果显示方式,使程序读起来更自然。
- 输入数字,定义运算规则:(0为加 1为减 2为乘 3为除"),输出结果
- 编写两个函数,分别求由键盘输入两个整数的最大公约数和最小公倍数。用主函数调用这两个函数,并输出结果
- 文字计算器 对输入的加减乘除等四则运算得出结果,是否考虑负数,小数等(难度:★★★★)由自己决定
- 编写功能要求按照考试成绩的等级输出百分制分数段,A等为85分以上,B等为70~84分,C等为60~69分 ,D等为 60分以下 。成绩的等级由键盘输入。