科学计算器学生时代小作品源码(C++版)
/*************************************************************************************
简易科学计算器
made by:danyuan
专业:12级——信息工程2班
学校:湖南理工学院
//功能说明:
1.可以进行带括号的表达式运算;
2.可以检查并识别更改表达式错误:
(1.对于非法字符,报错处理
(2.对于括号不匹配,报错处理
(3.对于数字或右括号接左括号,自动在中间补乘号
3.可以进行负数运算;
中缀表达式规则///
操作符特性:
0.表达式第一个字符不能为*、/、^、!、)
1.+、-、*、/这类操作符不能连续两个在一起,后面不能接')'、'!'
2.'('后面不能接+、*、/、!这些符号,如果后面是‘-’,则认为是负号
3.'!'后面不能接数字,可以接其它操作符,如果不是!和^则在中间自动补乘号
4.'^'后面不能接任何操作符除了‘(’
5.单向右操作符后面只能接同类操作符或数字、(。例如:开方、取反、求倒数、三角函数等
6.单向左操作符后面不能接数字,如果是同类操作符继续操作,如果是‘(’或其他操作符中间添加乘号
7.括号必须配对
总结:大体目前可以分为四类:
即:接双操作数的操作符、接左操作数的单操作符、接右操作数的单操作符、双向都可接的单操作符(此类
暂不谈论,看情况而定)
*************************************************************************************/
先上图:
当然这是美化后的科学计算器,下面是基础计算器的代码:
#ifndef CACULATOR_H
#define CACULATOR_H
#include "DYHeaderFile/stack.h"
template<class T>
class DYCaculator{
protected:
char *middlestr;//中缀式字符串
char *backstr;//后缀式字符串
T m_result; //存放运算结果
stack<T> numstack;//用于存放操作数的栈
stack<char> operatestack;//用于存放操作符号的栈
static char m_symbolarr[255];//基本计算器能识别的符号
public:
/工具类函数///
static T factoria(T num);//阶乘,特点:接左操作数的操作符!表示和^同级
/框架计算函数/
DYCaculator(char *str);
DYCaculator()//重载构造函数
{
middlestr=NULL;
backstr=NULL;
m_result=0;
}
~DYCaculator();
virtual bool SetText(char * a);
T GetResult();
bool GetBackStr();
virtual bool Caculating();
virtual T DealFun(char c);
virtual bool CheckOperator();
virtual void SetSinFlag(bool flag)=0;//设置角度还是弧度运算
//工具函数/
virtual int GetClass(char a);//返回分类
int JungdeLevel(char a);//判断优先级
int GetNumClass(char a);//返回分类,只为GetNum函数服务
T GetNum(char *str);//根据字符串返回其数值
void IverseStr(char *str);
void Ttostr(T num,char *str);//从某个数值转换为字符串
bool strequal(char *a,char *b);//忽略大小写的字符串判等函数
};
//工具函数
template<class T>
int DYCaculator<T>::GetClass(char a)//返回分类
{
char arr1[]="1234567890";
char arr2[]="+-*/()^!";//同类不能叠加(特殊另作处理),后可以接子类操作符
int flag=-1,i;
for(i=0;i<(int)(strlen(arr1));i++)
if(a==arr1[i])
flag=1;//表明是数字
if(a=='.')
flag=2;//表明是小数点
for(i=0;i<(int)(strlen(arr2));i++)
if(a==arr2[i])
flag=3;//表明是操作符
//子类可重写单操作符的处理
return flag;
}
template<class T>
char DYCaculator<T>::m_symbolarr[255]="0123456789+-*/()^.!";//abcdefghijklmnopqrstuvwxyz//a~z代表可以支持26个附
加操作或函数
template<class T>
DYCaculator<T>::DYCaculator(char *str)//生成对象时,传入一个操作指令,进行分配内存、获得操作指令
{
middlestr=new char[strlen(str)*2];
strcpy(middlestr,str);
backstr=new char[2*strlen(str)];
backstr[0]='\0';
m_result=0;
}
template<class T>
bool DYCaculator<T>::SetText(char * str)//后面调用,传入一个操作指令,进行分配内存、获得操作指令
{
if(middlestr)//如果原来已经分配了内存
delete []middlestr;//释放原有内存
if(backstr)
delete []backstr;
middlestr=new char[strlen(str)*2];
if(!middlestr)
return false;//若分配内存失败,则报错
strcpy(middlestr,str);
backstr=new char[2*strlen(str)];
if(!backstr)
return false;//若分配内存失败,则报错
backstr[0]='\0';//清空该字符串
m_result=0;//初始化
numstack.clear();
operatestack.clear();
return true;
}
template<class T>
DYCaculator<T>::~DYCaculator()//释放动态分配的内存
{
delete []middlestr;
delete []backstr;
}
template<class T>
bool DYCaculator<T>::CheckOperator()//检验输入表达式合法性
{
stack<char> teststack;//匹配'('、')'
int i=0,j=0;
int len=strlen(middlestr);
int symbolstrlen=strlen(m_symbolarr);
bool chflag=false;
//0.第一个字符不能为*、/、^、!、)
char arr[6]="*/^!)";
for(i=0;i<5;i++)
if(middlestr[0]==arr[i])
return false;
for(i=0;i<len;i++)
{
//1.检查是否有非法字符出现在表达式中
chflag=false;//合法符号标志位
for(int m=0;m<symbolstrlen;m++)
if(middlestr[i]==m_symbolarr[m])
chflag=true;
if(!chflag)
return false;
//2.利用栈判断括号是否配对
if(middlestr[i]=='(')
teststack.push('(');
if(middlestr[i]==')')
{
if(teststack.empty())//如果没有左括号,则表达式有误
return false;
teststack.pop();//弹出'('
}
//3.自动添加乘号,即:1.')'紧接'('、 2.数字紧接'('、 3.')'接数字、4.数字接右单操作符, 中间自动补
乘号
int addflag=false;
if(GetClass(middlestr[i])==1||(middlestr[i]==')'))//如果是数字
{
if(middlestr[i+1]=='(')//||((middlestr[i]==')')&&(GetClass(middlestr[i+1])==1))||
((GetClass(middlestr[i+1])==4)))//则判断为乘号,自动为表达式添加乘号
addflag=true;
if(middlestr[i]==')'&&(GetClass(middlestr[i+1])==1||(GetClass(middlestr[i+1])==4)))//右
括号后接数字或左单向操作符添乘号
addflag=true;
if(GetClass(middlestr[i])==1&&(GetClass(middlestr[i+1])==4))
addflag=true;
}
if(middlestr[i]=='!'&&(GetClass(middlestr[i+1])==1||(middlestr[i+1]=='(')))//'!'后面接数字和'('
自动补乘号
{
addflag=true;
}
//为有需要的地方添加乘号,属于步骤3
if(addflag)
{
int k=0;
for(k=len;k>i;k--)//元素后移,腾出空位
{
middlestr[k+1]=middlestr[k];
}
middlestr[++k]='*';//添加乘号
middlestr[++len]='\0';
// cout<<middlestr<<endl;//测试之用
}
//4.检查是否有操作符紧接操作符(预判操作i+1),即:四则运算符之间要隔开、四则运算符不能接')'等
等
if(GetClass(middlestr[i])==3)//如果是操作符
{
if(middlestr[i]=='!')
{
if(GetClass(middlestr[i+1])==1)//阶乘符号后面不能接数字
return false;
}
else if(middlestr[i]=='(')//左括号后面不可直接接双操作符
{
if(GetClass(middlestr[i+1])==3&&(middlestr[i+1]!='(')&&(middlestr[i+1]!=')')&&
(middlestr[i+1]!='-'))
return false;
}
else if(middlestr[i]==')')//右括号所有操作符都可以接
{
}
else
{
if(GetClass(middlestr[i+1])==3&&(middlestr[i+1]!='('))
return false;//如果四则运算符号后面紧接四则运算符号,则表达式有误
}
}
//5.如果是单操作符,方式又不一样了
if(GetClass(middlestr[i])==4)
{
//1.单操作符后不能接操作符(除‘(’)
if(GetClass(middlestr[i+1])==3&&(middlestr[i+1]!='(')||(GetClass(middlestr[i+1])==4))
return false;
}
}
if(!teststack.empty())
return false;//如果栈不为为空,则配对失败,此操作属于步骤2
return true;
}
template<class T>
T DYCaculator<T>::GetResult()
{
cout<<"有什么不懂的,或是想交流编程心得的人";
cout<<endl<<"欢迎大家"<<"加我"<<"企鹅群:320540648"<<endl;
return m_result;
}
template<class T>
bool DYCaculator<T>::GetBackStr()//获得后缀表达式
{
int i=0,j=0,flag=0;
int len=strlen(middlestr);
int reallen=0;//后缀式字符串实际长度
bool separetflag=0;
for(i=0;i<len;i++)
{
flag=GetClass(middlestr[i]);
if(flag==3||(flag==4))//一. 如果是操作符,就判断优先级入栈或出栈
{
//(1.分隔两个操作数字符串
if(separetflag==1)//如果前面有数字,添加分隔符
{
backstr[reallen++]='|';
separetflag=0;
}
//(2.对操作符优先级的判断,即:
//1.前后两操作符之间的优先级判断(前者先出栈,若后者优先级高,则后者入
栈,否者前者出栈);
//2.若遇到左括号,直接入栈(优先级最高),遇到右括号,操作符连续出栈,
直到弹出一个左括号为止,弹出的四则运算符添加到后缀式末尾;
//3.智能识别负号,用#号代替负号
if(operatestack.empty())//如果操作符栈为空,则直接入栈
operatestack.push(middlestr[i]);
else
{
if(middlestr[i]=='(')//如果是左括号,且后面是减号,则为后面的数添加一个负号
{
operatestack.push(middlestr[i]);//先入栈
if(middlestr[i+1]=='-')
{
backstr[reallen++]='#';//代表这是负号,不是减号
i++;
continue;
}
}
else if(middlestr[i]==')')//如果为右括号,则出栈,直到弹出一个左括号为止
{
while(!operatestack.empty()&&(operatestack.gettop()!='('))//依次弹出栈
内的符号
{
backstr[reallen++]=operatestack.gettop();
operatestack.pop();
}
operatestack.pop();//弹出'('
}
else
{
//如果栈内符号优先级大于当前符号,则依次出栈
while(!operatestack.empty()&&(JungdeLevel(operatestack.gettop())
>=JungdeLevel(middlestr[i]))&&(operatestack.gettop()!='('))
{
backstr[reallen++]=operatestack.gettop();
operatestack.pop();
}
//否则就入栈
operatestack.push(middlestr[i]);
}
}
}
else//二.是操作数中的字符
{
backstr[reallen++]=middlestr[i];
separetflag=1;
}
}
while(!operatestack.empty())//如果栈内还有操作符,则依次全部出栈
{
backstr[reallen++]=operatestack.gettop();
operatestack.pop();
}
backstr[reallen]='\0';
return true;
}
template<class T>
bool DYCaculator<T>::Caculating()//计算后缀式并获得结果
{
GetBackStr();//转为后缀式
int i=0,j=0;
int pos=0;
int backreallen=strlen(backstr)+1;
char arr[100];
bool numflag=false;
for(i=0;i<backreallen;i++)
{
//1.如果是操作符,则将栈顶两个元素取出进行四则运算
if(GetClass(backstr[i])==3||(GetClass(backstr[i])==4))//如果是四则运算符号
{
if(numflag)//如果后面是操作符,则将操作数入栈
{
numflag=false;
arr[j]='\0';
numstack.push(GetNum(arr));//根据字符串获得数值并将数值压入栈中
j=0;//初始化
}
numstack.push(DealFun(backstr[i]));//将运算结果压入栈中
}//2.如果是分隔符或结尾符,判断其前面是否有操作数字符串,如果有就将操作数字符串转化为数字后入栈
else if(backstr[i]=='|'||(backstr[i]=='\0'))//如果后面是分隔符或结尾符,将操作数入栈
{
if(numflag)//将操作数入栈
{
numflag=false;
arr[j]='\0';
numstack.push(GetNum(arr));
j=0;//初始化
}
}
else //3.生成数字字符串,即:用一个字符串来保存操作符含有的字符
{
if(backstr[i]=='#')//代表负数,如果是负数,前面加负号
{
arr[j++]='-';
continue;
}
arr[j++]=backstr[i];//如果是数字,则加入数字字符串
numflag=true;//代表有数字还未入栈
}
}
m_result=numstack.gettop();
return true;
}
template <class T>
T DYCaculator<T>::DealFun(char c)
{
bool doubleOrSingle=false;//初始化为双操作数
T operate1=0,operate2=0,result=0;
//判断是单操作数还是双操作数.....(单操作数只需弹出一个操作数,否则两个)
if(c=='!')
doubleOrSingle=true;
if(doubleOrSingle)//单操作数的情况
{
if(c=='!')
{
result=factoria(numstack.gettop());
numstack.pop();
}
}
else//此处是双操作数的情况
{
if(!numstack.empty())
{
operate1=numstack.gettop();
numstack.pop();
}
else
{
if(c=='-'||(c=='+'))
operate1=0;
else
operate1=1;
}
if(!numstack.empty())
{
operate2=numstack.gettop();
numstack.pop();
}
else
{
if(c=='-'||(c=='+'))
operate2=0;
else
{
operate2=operate1;
operate1=1;
}
}
switch(c)
{
case '-':result=operate2-operate1;break;
case '+':result=operate2+operate1;break;
case '*':result=operate2*operate1;break;
case '/':
if(operate1!=0)
result=operate2/operate1;
else
return 0;//分母为零时操作失败
break;
case '^':result=pow((double)operate2,(double)operate1);break;
}
}
return result;
}
//计算功能函数//
template<class T>
T DYCaculator<T>::factoria(T num)//阶乘,特点:接左操作数的操作符!表示和^同级
{
if(num<=0)
return (T)0;
if(num==1)
return 1;
for(int i=(int)(num-1);i>0;i--)
num*=(T)i;
return num;
}
//工具函数//
template<class T>
int DYCaculator<T>::JungdeLevel(char a)//判断优先级
{
char arr[]="abcdefghijk";//比*、/优先级高
for(int i=0;i<(int)(strlen(arr));i++)
if(a==arr[i])
return 3;
if(a=='+'||(a=='-'))
return 0;
else if(a=='*'||(a=='/'))
return 2;
else if(a=='^'||a=='!')
return 4;
else
return 5;//否者为括号,为*
}
template<class T>
int DYCaculator<T>::GetNumClass(char a)//返回分类,只为GetNum函数服务
{
char arr1[]="1234567890";
char arr2[]=".";
int flag=-1,i;
for(i=0;i<(int)(strlen(arr1));i++)
if(a==arr1[i])
flag=1;//表明是数字
if(a=='.')
flag=2;//表明是小数点
return flag;
}
template<class T>
T DYCaculator<T>::GetNum(char *str)//根据字符串返回其数值
{
T result=0;
T t1=0,t2=0;
int len=strlen(str);
int i=0,flag=0,j=1;
for(i=0;i<len;i++)
{
if(GetNumClass(str[i])==2)
flag=1;
if(GetNumClass(str[i])==1)
{
if(!flag)
t1=t1*10+(str[i]-'0');
else
{
t2=t2*10+(str[i]-'0');
j*=10;//判断小数点向后移动的位数
}
}
}
result=t1+(t2/j);//整数部分加上小数部分
if(str[0]=='-')
result*=-1;
return result;
}
template<class T>
void DYCaculator<T>::IverseStr(char *str)//字符串翻转
{
int i=0,len=strlen(str);
char c;
for(i=0;i<((len+1)/2);i++)
{
c=str[i];
str[i]=str[len-i-1];
str[len-i-1]=c;
}
}
template<class T>
void DYCaculator<T>::Ttostr(T num,char *str)//从某个数值转换为字符串,存在精度损失,不可取
{
int i=0,j=0;
int tnum=(int)num,tmod=1;
while(tnum>0)
{
str[i++]=tnum%10+'0';//返回字符
tnum/=10;
}
str[i]='\0';
IverseStr(str);
str[i++]='.';
for(j=0;j<6;j++)//精确到小数点后6位即可
{
tnum=(int)(num*10)%10;
str[i++]=tnum+'0';
num*=10;
}
str[i]='\0';
}
template<class T>
bool DYCaculator<T>::strequal(char *a,char *b)//忽略大小写的字符串判等函数
{
int i=0,j=0;
bool flag=true;
if(strlen(a)!=strlen(b))//如果长度不等,则不相等
return false;
for(i=0;a[i]!='\0'&&(b[i]!='\0');i++)
{
//忽略大小写进行比较
if(a[i]!=b[i]&&(a[i]!=(b[i]+('A'-'a')))&&(a[i]!=(b[i]-('A'-'a'))))
{
return false;
}
}
return true;
}
#endif