C语言实现 计算器(堆栈)

/********************************************************************
* 用堆栈做的计算器程序 *
********************************************************************/



/********************************************************************

本程序功能:实现用堆栈处理计算表达式
具体内容:
I: 如果算式里面有计算式不应该出现的字符,则将其智能略去 如:将(1.4a54+2f.6)*3.09s+4ff看成(1.454+2.6)*3.09+4
II: 检查括号是否匹配,如果匹配,再检查是否出现在合法位置 如:(8*(7-4)不匹配,以及65*(72+98)(70-45)匹配但是不合法
III: 检查计算数与计算符号的数量是否合格 如:+23-4* 、23-4*、+23-4 等等
IV: 检查输入小数的时候小数点是否输入过多以及智能改正 如:将3....2*10+8 看成3.2*10+8
V: 检查连续输入大于两个符号时候是否能智能修改,“不能”则-> 对于3+-2 给出判断结果:输入有误
VI: 接V:如果判断能够改正,则-------------------------------> 将4++++++++++++5看成4+5并计算
VII:检测“0”是否出现在分母上

【下边是检测数据,检测结果与期望结果吻合】
【注:输入数据结尾没有“#”,以为在我的程序里面要它没用】
()*2+3# 期望结果:输入有误
(3+3)*3+1*(1+3)-3/2+3*3# 期望结果:29.5
1+2*(4-5)+45# 期望结果:44.000000
(1.454+2.6)*3.09+4# 期望结果:16.5269
(1.4a54+2f.6)*3.09s+4ff# 期望结果:16.5269

(56-23)/8-4# 期望结果:0.125
34+p(u89-12.3)k/3# 期望结果:59.5667
89.5*749+25)# 期望结果:输入有误
89.5*749+25# 期望结果:67060.500000
(8*(7-4)# 期望结果:输入有误

65*(72+98)(70-45) # 期望结果:输入有误
6*# 期望结果:输入有误
)5+3(# 期望结果:输入有误
(3+)(4)# 期望结果:输入有误
3....2*10+8# 期望结果:40

+23-4*# 期望结果:输入有误
23-4*# 期望结果:输入有误
+23-4# 期望结果:输入有误
3+-2# 期望结果:输入有误
4+++++++++++++++5# 期望结果:9
********************************************************************/



/********************************************************************
* 程序中所用到的头文件 *
********************************************************************/

#include<iostream>

#include<cstring>

using namespace std;

/********************************************************************
* 宏定义 *
********************************************************************/

//#define Stack stack //之所以不直接用stack是因为我打算完成整个程序后自己练习写个"Stack.h"

#define MAX_LENGHT 50

//预先
假设计算字符串最多有500个字符

#define MAX 10 //一般情况下输入要计算的数不会超过10位

/********************************************************************
* 自定义函数 *
********************************************************************/

bool isOperand(char ch); //检查是否为操作数

int priority( char op); //设置操作等级并返回操作符op的等级

template <class Stack_type> //用计算符号op计算数字number_1 和 number_2
Stack_type caculate( char op, const Stack_type number_1, const Stack_type number_2 );

char *mark(char string[]); //将符号和数字放在同一个堆栈里面时,两个数字可能会连在一起,导致无法区分
//哪个是第一个数哪个是第二个数,这里我用了小方法,就是在每个数字后边加个
//字母a来将两个连着的数字区分开

char *parsing(char A_string[]); //将将传入的中缀计算字符串A_string转化成后缀计算字符串并返回一个指针

float result(char A_string[]); //计算用函数“char *parsing(char A_string[]);”转化过的后缀计算字符串的值

bool check(char A_string[]);


template<class Type>class Stack;

template<class Type>
class StackNode
{
friend class Stack<Type>;
private:
Type data;
StackNode<Type> *link;
StackNode(Type D=0,StackNode<Type> *L=NULL):link(L),data(D){}
};

template<class Type>
class Stack
{
public:
Stack():_top(NULL),NumItem(0){}
void push(Type item);
void pop(); //Type pop();
Type top();
void MakeEmpty();
bool empty();
private:
int NumItem;
StackNode<Type> *_top;
};

template<class Type>
void Stack<Type>::push(Type item)
{
_top=new StackNode<Type>(item,_top);
NumItem++;
}

template<class Type>
void Stack<Type>::pop()
{
StackNode<Type> *p;
p=_top;
_top=_top->link;
delete p;
NumItem--;
}

template<class Type>
Type Stack<Type>::top()
{
return _top->data;
}

template<class Type>
bool Stack<Type>::empty()
{
return _top==NULL;
}

template<class Type>
void Stack<Type>::MakeEmpty()
{
delete _top;
}
/********************************************************************
* 主函数 *
********************************************************************/

int main()//主函数
{
char inputString0[MAX_LENGHT]; //定义输入的字符串

char inputString[MAX_LENGHT]; //为了不改变输入的字符串,用来充当计算过程中的inputString0
while(1){
gets( inputString0 ); //从键盘接收输入的计算字符串inputString0

strcpy( inputString , inputStri

ng0 ); //为了不改变输入的字符串inputString0,用inputString来充当计算过程中的
//inputString0.以后计算
用到计算字符串的时候都将inputString传递,而原始字符串
//inputString0不变
if(check(inputString)==false)
{
cout <<"输入有误!"<<endl;
//exit(0);
}
else
{
strcpy( inputString , mark(inputString) ); //进行标记inputString并且返回赋值给inputString

strcpy( inputString , parsing(inputString) ); //将【经过处理】的中缀计算字符串返回并且赋值给inputString

cout <<"最终结果:"<<result(inputString)<<endl;
} //计算最终的结果
}
return 0;
}

/********************************************************************
*函数名称:isOperand *
*函数表述: 检查ch是否为操作数 *
*输入值: char ch *
*返回值: 检查ch是否为操作数,是则返回1,否则返回0 *
********************************************************************/

bool isOperand(char ch)
{
bool ret; //ret :标记是否为操作数
switch(ch)
{
case '+':
case '-':
case '*':
case '/':
case '(':
case ')':
ret = false; //ret=0 :ch不是操作数
break;
default :
ret = true; //ret=1 :ch是操作数
break;
}
return ret;
}

/********************************************************************
*函数名称:priority *
*函数表述: 为操作符op (+、-、*、/)设置操作等级并返回操作符op的等级*
*输入值: char op *
*返回值: 返回操作符op的等级 *
********************************************************************/

int priority(char op)
{
int p; //p表示操作符号的等级
switch(op)
{
case '+':case '-':
p = 1;
break;
case '*':case '/':
p = 2;
break;
default:
p = 0;
break;
}
return p;
}

/********************************************************************
*函数名称:mark *
*函数表述: 在每个数字后边添加a *
*输入值: char string[] *
*返回值: 返回添加字符a后的字符串 *
********************************************************************/

char *mark(char string[])
{
int i;
int j;
int lenght = strlen(string); //lenght :输入的计算字符串string[]的长度
char A_string[MAX_LENGHT]; //添加字母a 之后的新字符串,MAX_LENGHT初步宏定义为500,基本上已经足够了

for(i = 0,j = 0; i < lenght;) //从计算字符串string[]的第0为开始搜索到全部搜索过为止
{
if( isOperand(string[i]) || string[i]=='(' ) //如果string[i]是数字或者“(”,则直接复制给新字符串A_string

[i]
A_string[j++] = string[i++];
else if(string[i]==')') //如果string[i]是“)”,则前边一定是数字的最后边,
{
A_string[j++] = 'a'; //此时给那个数字最后添加一个作为标记的符号
“a”

A_string[j++] = string[i++]; //这个是将右括号复制给A_string

A_string[j++] = string[i++]; //这里赋值是因为任何计算中,右括号后边第一个一定是计算符号,不用判断是否为数
//字了,(因为这个函数的用途:见函数表述)所以这里直接复制给A_string
}
else
{
A_string[j++] = 'a'; //这个就不解释了,上边解释过了

A_string[j++] = string[i++];
}
}
if( isOperand(A_string[j]) ) //这里的原因是:当字符串的后边有一个数字,比如:A+B的时候,B后边没有操作符
A_string[j++] = 'a'; //号了 ,就不能正常给数字B后边添加字母a了
A_string[j] = '\0'; //最后一定要给新的字符串一个结尾符
return A_string;
}

/********************************************************************
*函数名称:parsing *
*函数表述: 将中缀字符串转化为后缀计算字符串 *
*输入值: char A_string[] *
*返回值: 返回转化完的后缀计算字符串 *
********************************************************************/

char *parsing(char A_string[])
{
int i;
int j;
char ch_0;
char ch_1;
int lenght = strlen(A_string); //输入的计算字符串的长度
char B_string[MAX_LENGHT];
Stack <char>setStack ;

for( i = 0, j = 0; i < lenght; i ++)
{
ch_0 = A_string[i];
if( isOperand(ch_0) ) //如果是操作数,则先存放在数字栈numbers_Stack
B_string[j++] = ch_0; //setStack.push(ch_0);
else
{
if( ch_0 == '(' ) //左括号的时候--------
{
setStack.push(ch_0); //左括号直接压栈
}
else if( ch_0 == ')' ) //右括号的时候--------
{
while( !setStack.empty() )
{
ch_0 = setStack.top(); setStack.pop(); //弹出栈的最上边元素
if( ch_0 == '(' )
break;
else
B_string[j++] = ch_0; //只要没有遇到左括号,就一直弹出栈内元素
}
}
else //既不是左括号右不是右括号的时候---------
{
if( !setStack.empty() ) //如果栈不是空的话
{
do
{
ch_1 = setStack.top(); setStack.pop(); //弹出栈的最上边元素
if( priority( ch_0 ) <= priority( ch_1 ) ) //当想入栈的元素级别 小于 栈内顶元素运算级别时
{
B_string[j++] = ch_1; //弹出栈顶元素
if(setStack.empty()) //当栈内没有元素了的时候,就不用比较了,直接入栈跳出去就OK了
{
setStack.push(ch_0);
break;
}
}
else
{
setStack.push(ch_1); //如果想入栈的元素的优

先级别大于栈顶元素级别,则将取出的元素和它都压入栈内
setStack.push(ch_0);
break;

}
}while( !setStack.empty() );
}
else //如果是空的话,就不用比较栈内元素和要入栈的元素级别了,直接入栈
{
setStack.push(ch_0);
}
}
}
}
while( !setStack.empty() ) //当栈内还有元素的时候,一起将所有元素弹出
{
B_string[j++] = setStack.top(); setStack.pop();
}
B_string[j] = '\0'; //给B_string加结尾符
return B_string;
}

/********************************************************************
*函数名称:caculate *
*函数表述: 用计算符 op 计算 number_1 和number_2 *
*输入值: op、number_1 、number_2 *
*返回值: 用计算符 op 计算 number_1 和number_2的结果 *
********************************************************************/

template <class Stack_type>
Stack_type caculate( char op, const Stack_type number_1, const Stack_type number_2 )
{
Stack_type temp;
switch(op)
{
case '+':
temp = number_1 + number_2;
break;
case '-':
temp = number_2 - number_1; //这里很容易出错,一定是栈下边减去栈上边的
break;
case '*':
temp = number_1 * number_2;
break;
case '/':
temp = number_2 / number_1; //这里很容易出错,一定是栈下边除以栈上边的
break;
}
return temp;
}
/**********************************************************
用这种写法也可以,但是这样的话会有一个waring,所以没有这样写
**********************************************************/
/*
template <class Stack_type>
Stack_type caculate( char op, const Stack_type number_1, const Stack_type number_2 )
{
switch(op)
{
case '+':
return number_1 + number_2;
case '-':
return number_2 - number_1;//这里很容易出错,一定是栈下边减去栈上边的
case '*':
return number_1 * number_2;
case '/':
return number_2 / number_1;//这里很容易出错,一定是栈下边除以栈上边的
}
}
*/

/********************************************************************
*函数名称:result *
*函数表述: 计算后缀计算字符串 *
*输入值: char A_string[] *
*返回值: 后缀计算字符串的结果 *
********************************************************************/

float result( char A_string[])
{
int i, j, k;
Stack <float> setStack; //数字栈/*接下来先把A_string[]里面的所有数字用浮点型堆栈setStack储存起来*/
char numString[MAX]; //用atof将它转为float型再保存在堆栈里,一般情况下输入的数字不会大于10^10
float numNumber; //用atof将numString转化后传给numNumber,将float型numNumber压入栈中方便计算
float t

emp[2]; //接收需要计算的时候 从栈里面弹出来的那两个float型元素
float sum; //存储temp[0] 和 temp[1] 的计算结


for(i = 0, j = 0, k = 0; i < strlen(A_string); i ++)
{
if( isOperand(A_string[i]) && A_string[i]!='a' ) //当正在读取的是数字 并且 没有读完这个数字的所有位数的时候,
{
numString[j++] = A_string[i]; //将正在读取的数字元素复制给numString
}
else
{
numString[j] = '\0'; //否则,当这个数字的所有位都读进numString[]的时候,给numString[]结尾标志,并
numNumber = atof(numString); //切将它转换为float型,用float型的numNumber接收
if(A_string[i]=='a') //如果是因为遇到数字结尾标记字母“a”而进入else的话,将它直接压入堆栈,因为
{
setStack.push(numNumber); //此时这个数字后边的一定也是数字,而不是操作符。
j = 0; //从新记录下一个数字字符串
}
else //否则的话,如果A_string[i]!='a',则是因为遇到了操作符进入这里的,那么就需
{ //要进行弹出并且计算了
if( !setStack.empty()) //这个为了严禁才写的,不过一般情况下进入这里就不能是空的
{
temp[0] = setStack.top();setStack.pop();
temp[1] = setStack.top();setStack.pop();
}
sum = caculate( A_string[i], temp[0], temp[1] ); //计算结果,用操作符A_string[i]计算temp[0] 和 temp[1]
setStack.push(sum); //将结果压栈
}
}
}
return sum;
}

/********************************************************************
*函数名称:check *
*函数表述: 检查输入的字符串A_string[]是否合法 *
*输入值: A_string[] *
*返回值: 合法则返回true,否则返回false *
********************************************************************/

bool check(char A_string[])
{
char B_string[MAX_LENGHT];
int i,j;
char c, d;
int length = strlen(A_string);
int sign = 0;
int numSumbers = 0;
int opeSumbers = 0;
/*将除了数字、+、-、*、/、(、)、. 以外的过滤一下*/
for(i = 0, j = 0; i <= length - 1 ; i++ )
{
c = A_string[i];
if ( c>='0'&&c<='9' || !((c-'+')*(c-'-')*(c-'*')*(c-'/')*(c-'(')*(c-')')*(c-'.')) )
{
if ( c == '(' )sign ++;
else if (c ==')' )sign --;
else if ( c>='0'&&c<='9' );
else if ( c == '.' ){if( A_string[i-1] == '.') continue;}
else if ( c == B_string[j-1] ) continue; //比如写:2++4这种重复写运算符的情况,或者2...4+2*3这种重复写多个小数点的情况
else opeSumbers ++;

if ( sign < 0 ) return false;
else B_string[j++] = c;
}
}
B_string[j] = '\0'

;
length = strlen(B_string);
for(i = 0; i <= length - 2 ; i++ )
{
c = B_string[i];
if ( c>='0'&&c<='9' )
{
d = B_string[i+1];
if( !((d-'+')*(d-'-')*(d-'*')*(d-'/')*(d-')')) )
numSumbers ++;
}
else
{
d = B_string[i+1];
if( c =='(' || c ==')')
if( d =
='(' || d ==')')return false;
}
}
if( B_string[i] >='0' && B_string[i] <='9' )numSumbers ++;

if(sign != 0) return false;
else if ( numSumbers - opeSumbers != 1 )return false;

length = strlen(B_string);
for(i = 0; i < length; i++ )
{
A_string[i] = B_string[i];
}
A_string[i] = '\0';
return true;
}


相关文档
最新文档