表达式计算C++.docx
《表达式计算C++.docx》由会员分享,可在线阅读,更多相关《表达式计算C++.docx(18页珍藏版)》请在冰豆网上搜索。
表达式计算C++
表达式计算-C++
1,这是使用C++编写的一个表达式转后缀式再求值的一个类
2,由于本人只是学生一个,这些都是为了验证学习上的东西,写得比较粗糙,只供初学者参考
3,本类可以计算小数,负数。
只能识别+-*/%^(^这是一个乘方运算符,用法如:
2^16计算2的16次方,%这是一个求余运算符,使用规则和C++编译器类似),只能包含()这一种括号,并且在操作数和括号之间的运算符不能省略,每个运算符要明确给出
4,本类负数处理的方法是采用:
符号来替换,可以在表达式中输入(-)负号,但在字符检测函数来区分负号时,如果检测出来是负号就会自动转换为(:
)这个符号。
这主要是为了在以后计算时可以不用每次来区分是不是负号,而只区分一次即可。
也可以直接在表达式中用:
这个符号来表示负号,识别函数可以直接识别而不需要区分和替换。
要注意的是,(-)这种负号一定要使用表达式检查函数才能识别,如果不想使用检查函数,就只能用:
来代替。
5,本类的表达式检查函数会按照一定的规则来检测表达式输入是否合要求,如果有不合要求的字符或字符位置不合要求都会报错,并会指出是那一个字符,而且当函数检测到一个错误时不会立即停下,而会继续检测,只到表达式结束。
并输出表达式中的那些字符不合要求。
6,单个0是不能出现在表达式中,但0.0可以,如果表达式中需要出0可以用0.0代替,但如:
30/0.0这样的计算会在计算时给出错误提示。
7,表达式中可以出现-(-(-13+5)+5)这样的深层负号计算,计算结果和VC++编译器结果一样
8,本类在VC6.0,w7系统下编译通过并运算。
9,本类中没有主动清空数据成员的方法,所以有使用时,要将类的对象定义在计算循环或函数内,这样每一次计算都会对对象进行创建和析构,不然会出现异常,当然你可以增加一个清空数据成员的方法。
//以下是ExpEvaluate.h类的头文件
#ifndefExp_h
#defineExp_h
#include//字符串类头文件
#include//STL栈头文件
#include//STL队列头文件
#include//STL向量头文件
#include//STL链表头文件
usingnamespacestd;
classExpEvaluate
{
public:
ExpEvaluate();//用于初始化错误列表
voidExpInto(string&Exp);//输入一个表达式
intExpDetector();//字符串规则检测函数
intExpIdentification(charExp);//数字运算符识别函数
intExpPriority(charOpe);//返回运算符的优先级
voidExpSuffix();//将表达式转为后缀式
doubleExpCompute();//计算后缀表达式,并返回结果
voidErrorBacktrack();//根据错误号返回相应错误
private:
stringexps;//表达式字符串
stackoper;//运算符栈
stackoperand;//操作数栈
queuepostfix;//后缀队列
vectorerror;//错误列表
listindex;//错误索引表
intgrade;//错误标号
stringerrorout;//错误输出缓冲区
doubleOpReadop();//读取操作数,并返回转换后的结果
doubleOpOrder(doubleop,doublex);//求op数的x次方
};
#endif
//以下是.cpp实现文件
#include"ExpEvaluate.h"
#include"resource.h"
#include//字符转换数字头文件
#include
#include
ExpEvaluate:
:
ExpEvaluate()
{
//初始错误列表
error.resize(15);//为列表初始为15个元素
grade=0;//初始标号为0,表示没有错误
error.at(0)="操作数错误:
操作数前不能是)右括号!
";
error.at
(1)="操作数错误:
0不可以出现在操作的开始,也不能作为一个单独的操作数!
";
error.at
(2)="操作数错误:
小数点不能在一个操作数中重复出现!
";
error.at(3)="运算符错误:
运算符前面必须是操作数或)右括号,并且运算符不能重叠!
";
error.at(4)="括号错误:
左括号前面必须是运算符或左括号或负号!
";
error.at(5)="括号错误:
右括号前面必须是操作数或右括号!
";
error.at(6)="括号错误:
括号输入不匹配!
";
error.at(7)="分隔符错误:
#号分隔符只能出现在表达式的边界位置!
";
error.at(8)="表达式错误:
表达式的结尾必须是操作数或右括号!
";
error.at(9)="负号错误:
负号前面必须是运算符或左括号呀分隔符!
";
error.at(10)="字符错误:
表达式中除数字,运算符,:
负号,()括号之外不能出现其它符号!
";
error.at(11)="计算错误:
除数不能为0,也不能小于0.0001(个人限制)!
";
error.at(12)="计算错误:
求余运算中,除数不能为0或小于0!
";
error.at(13)="计算错误:
次方运算中,次方数不能小于0!
";
error.at(14)="计算错误:
计算结果出现错误!
";
}
voidExpEvaluate:
:
ErrorBacktrack()
{
list:
:
iteratorpexp;//使用迭代器来遍历链表
charm[18];//字符缓冲区
if(grade==0)//如果错误标号为0表示没有错误,就直接返回
return;
elseif(grade==1)
{//如果为1表示错误由检测函数发现
for(pexp=index.begin();pexp!
=index.end();)
{//通过sprintf函数将错误的字符位置整数,格式化到字符缓冲区数组m中
sprintf(m,"错误字符位置:
%2d",*pexp);
errorout+=m;//将格式化的字符连接到错误输出缓冲区中
pexp++;
errorout+=error.at(*pexp);//将相对应的错误提示连接至缓冲区中
errorout+="\r\n";
pexp++;
}
cout<}
elseif(grade==2)
{//如果错误标号为2,表示是在计算时发现
for(pexp=index.begin();pexp!
=index.end();)
{//将相应的错误连接到错误缓冲区中
errorout+=error.at(*pexp);
errorout+="\r\n";
pexp++;
}
cout<}
}
intExpEvaluate:
:
ExpIdentification(charExp)//数字运算符识别函数
{
intiexp=(int)Exp;
if(iexp==46||iexp>=48&&iexp<=57)
return1;//表示识别到的是数字和小数点
switch(iexp)
{
case94:
case37:
case43:
case45:
case42:
case47:
return2;//表示识别到+-*/运算符
case40:
case41:
return3;//表示识别到括号
case35:
return4;//如果是#号,这只是一个分隔符,没有其它含意
case58:
return-1;//用这个符号来替换负号,表示识别到一个负号
}
return0;//表示识别到其它运算符
}
intExpEvaluate:
:
ExpPriority(charOpe)//返回运算符的优先级
{
switch(Ope)
{
case'^':
return4;//赋于次方运算符最高优先级
case'%':
case'*':
case'/':
return3;//赋于*,/,%侁先级
case'+':
case'-':
return2;
case'(':
return1;
}
return0;//如果不是运算符就返回0
}
voidExpEvaluate:
:
ExpInto(string&Exp)//输入一个表达式
{
if(Exp.empty())
{
return;
}
exps=Exp;//将表达式输入到类中
exps.insert(0,'#');//在表达式的边界加入一个分隔符,以简化规则检测
exps+='#';
return;
}
intExpEvaluate:
:
ExpDetector()//字符串规则检测函数,采用向前检测
{
inti=1,frequency=0;//frequency为小数点标志,每一个操作数能出现一位小数点
intsign=1;//如果出现错误就将标志位改为0,否则就不改变
charinterspace;
while(i{
interspace=exps.at(i);
switch(ExpIdentification(interspace))//判断每一个字符是否合规则
{
case1:
//如果是操作数
if(exps.at(i-1)==')')
{
index.push_back(i);//记录错误位置
index.push_back(0);//记录错误信息号
grade=1;//将标志位至1,说明是检测函数检测的错误
sign=0;//如果有错误就将返回标志至0
break;
}
elseif(interspace=='0')
{
//对0进行特别检测
if(ExpIdentification(exps.at(i-1))!
=1)
{//如果0出现在一个操作数的开始,那这个数必须为小数
if(exps.at(i+1)!
='.')
{//如果如果不是小数就算是非法数
index.push_back(i);
index.push_back
(1);
grade=1;
sign=0;
break;
}
}
}
elseif(interspace=='.')
{
//如果操作数中出现小数点,就必须将其标志,心防止出现多个小数点
if(frequency==1)
{
index.push_back(i);//记录错误位置
index.push_back
(2);//记录错误信息号
grade=1;//将标志位至1,说明是检测函数检测的错误
sign=0;//如果有错误就将返回标志至0
break;
}
else
frequency=1;//如果操作数中出现小数点就将标志置1
}
break;
case2:
//需要检测运算符是否合规则,并检测负号
frequency=0;//如果出现运算符就说明是另一个操作数,这时需要将小数点标志置0
if(interspace=='-')//如果是-号就需要判断是运算还是负号
{
if(ExpIdentification(exps.at(i-1))==2||exps.at(i-1)=='('||exps.at(i-1)=='#')
{
//如果-号前面是运算符或(括号或#号并且后面要是数字,这样就说明-号是负号
exps.replace(i,1,":
");//如果是负号就将它替换为:
号
break;
}
}
if(ExpIdentification(exps.at(i-1))!
=1&&exps.at(i-1)!
=')')
{
//如果运算符的前面不是操作数或不是)括号,
index.push_back(i);
index.push_back(3);
grade=1;
sign=0;
break;
}
break;
case3:
//需要检测括号是否合规则
if(interspace=='(')
{
if(ExpIdentification(exps.at(i-1))!
=2&&exps.at(i-1)!
=':
'&&exps.at(i-1)!
='('&&exps.at(i-1)!
='#')
{
//增加&&exps.at(i-1)!
=':
'这个条件就可以处理-(-(12+3)+3)这样的多层嵌套负号计算功能
index.push_back(i);
index.push_back(4);
grade=1;
sign=0;
}
oper.push(interspace);//是左括号就压栈
}
else
{
if(ExpIdentification(exps.at(i-1))!
=1&&exps.at(i-1)!
=')')
{
//如果右括号前面必须是操作数或右括号
index.push_back(i);
index.push_back(5);
grade=1;
sign=0;
}
if(oper.empty()||oper.top()!
='(')
{
index.push_back(i);
index.push_back(6);
grade=1;
sign=0;
}
else
oper.pop();//如果右括号匹配就将括号弹出
}
break;
case4:
//如果读到分隔符,这个在这里只能出现在边界
if(i!
=0&&i!
=exps.size()-1)
{
index.push_back(i);
index.push_back(7);
grade=1;
sign=0;
}
else
{
if(ExpIdentification(exps.at(i-1))!
=1&&exps.at(i-1)!
=')')
{
//如果结尾处不是操作数或右括号就出错
index.push_back(i);
index.push_back(8);
grade=1;
sign=0;
}
}
break;
case-1:
//如果读到负号
if(ExpIdentification(exps.at(i-1))!
=2&&exps.at(i-1)!
='('&&exps.at(i-1)!
='#')
{
index.push_back(i);
index.push_back(9);
grade=1;
sign=0;
}
break;
case0:
//如果读到非法字符就输出相应提示
index.push_back(i);
index.push_back(10);
grade=1;
sign=0;
break;
}
i++;
}
if(!
oper.empty())//如果括号匹配栈不为空就输出错误提示
{
index.push_back(i-1);//记录错误字符位置
index.push_back(6);//记录相对应的错误信息号
grade=1;//将标志修改为1,不示是检测函数发现的错误
sign=0;//将返回值标志至0,表示有错误
while(!
oper.empty())//如果不为空就将它清空
{
oper.pop();
}
}
returnsign;//sign为1表示表达式无错误,为0表示有错误
//当返回值为0,就调用错误输出函数来输出错误
}
doubleExpEvaluate:
:
OpOrder(doubleop,doublex)
{
intn=(int)x,i;//参数x之所以定为浮点型是为了简化操作
doublesum=1;
if(n==0)
{//0的任何次方都为1
return1;
}
for(i=0;i{
sum*=op;//获取op的x次方
}
returnsum;
}
voidExpEvaluate:
:
ExpSuffix()//将表达式转为后缀式
{
inti=0;
charinterspace,m;
while(i{
interspace=exps.at(i);
switch(ExpIdentification(interspace))
{
case1:
//如果读到的是操作数就直接入后缀
postfix.push(interspace);
break;
case-1:
//如果是负号就判断是否是-(12+3)之类的表达式
postfix.push(interspace);//先将负号直接入后缀
if(exps.at(i+1)=='(')//判断负号的下一位是不是左括号
{//如果下一位是括号,说明就是-(12+3)这种情况,遇到这种就将负号改为-0-
postfix.push('0');//加上0就将负号变为-0操作数了
postfix.push('#');//将负号变为-0操作数后要记得加上分隔符
oper.push('-');//并要加上一个减号,(这样可以达到负负得正,正负得负的目的)
//这里的思想是将-(12+3)替换为-0-(12+3),这样就可以处理-(-(-()))这样的负号
}
break;
case2:
//如果读到的是运算符就压入运算符栈
postfix.push('#');//连续操作数之间用#号隔开
if(oper.empty())
oper.push(interspace);//如果栈为空就直接压入栈
else//如果栈不空就要比较运算的优先级
{
while(ExpPriority(interspace)<=ExpPriority(oper.top()))
{
m=oper.top();//如果小于或等于就将栈中运算符弹出至后缀队列
postfix.push(m);
oper.pop();//弹出至后缀队列后就删除
if(oper.empty())
break;//如果运算符栈为空就结束循环
}
oper.push(interspace);//如果读到的运算符大于栈中运算符就直接压入
}
break;
case3:
//如果读到括号
if(interspace=='(')
oper.push(interspace);//如果读到的是左括号就直接入运算符栈
else
{
while(oper.top()!
='(')//如果读到右括号就在运算符栈中寻找左括号
{//并将左括号和右括号之间的运算符全部弹出至后缀队列
postfix.push(oper.top());
oper.pop();
}
if(oper.top()=='(')//最后再将左括号删除
oper.pop();
}
break;
}
i++;
}
while(!
oper.empty())//最后再将栈中的运算符全部弹出至后缀队列
{
m=oper.top();
postfix.push(m);
oper.pop();
}
}
doubleExpEvaluate:
:
OpReadop()//读取操作数,并返回转换后的结果
{
chardata[80];//用64个字符空间作为操作数缓冲区
inti=0;
doublef=0;
while(ExpIdentification(postfix.front())!
=2&&ExpIdentification(postfix.front())!
=4)
{
if(ExpIdentification(postfix.front())==-1)//如果是负号需要再转换
{
data[i]='-';
postfix.pop();//转换后要删除
i++;
if(postfix.empty())
break;//如果后缀为空就结束循环
}else
{
data[i]=postfix.front();
postfix.pop();//读取后就删除
i++;
if(postfix.empty())
break;//如果后缀为空就结束循环
}
}
data[i]='\0';//读完后要加上结束符
f=atof(data);//并转换
returnf;//转换后返回结果
}
doubleEx