编写了一个计算器小程序,作为栈的练习

这几天看了栈的结构,编写一个计算器小程序作为练习。

这个程序只能处理加法和乘法,只能输入整数,可以处理多重括号

编写过程中,感觉最有点混乱,主要是编程前期设计不够,边想边完成,导致结构和逻辑总是不清晰

中途重新设计了一遍,感觉清晰了一些,但是看着很复杂,不是特别优美。

在中缀表达式转后缀表达式的函数中,还增加了很多表达式形式是否正确的判断,大家可以测试一下还有没有未考虑到的情况,欢迎大家给我留言

这里先记录一下,然后后面再改进。

欢迎大家给我提意见和建议


/*******************************************
* 简易计算器程序(练习栈的应用)
* 功能:能计算 加法 和 乘法
*		可以使用括号
* 待改进的地方: 只能处理整数
				没有除法和加法的运算

* 实现过程:用户输入 -> 中缀表达式转换为后缀表达式
            -> 使用后缀表达式计算
			-> 返回结果

* 重点部分: 主要集中在中缀表达式转换为后缀表达式
			的函数infix2postfix中,包括如下:
			* 如何从string中分离数字和操作符
			* 如何保证表达式语法正确
			* 对不正确的表达式返回原因并停机

* 作者:FastestSnail 
* 时间:2016年12月

* 转载请注明出处
*******************************************/

#include<vector>
#include<string>
#include<sstream>
#include<iostream>
#include<iterator>
#include<algorithm>

using namespace std;

int cal(const string & s); //用于计算输入的表达式
const string infix2postfix(const string &s); // 把中缀表达式变成后缀表达式
void errQuit(const string &msg); // 用于输出错误信息,这些错误无法再被处理,直接停机
int main()
{
	string source;
	cout << endl;
	cout << " 请输入需要计算的表达式: ";
	cin >> source;	
	cout << " ---------------------------------------" << endl;
	cout << " 你输入的是: " << source << endl;
	cout << " ---------------------------------------" << endl;
	cout << " 结果为: " << cal(source) << endl;
	cout << endl;

	getchar();
	return 0;
}

void errQuit(const string &msg)
{
	cerr << "******\a" << msg << '\n'<< endl;
	exit(-1);
}
int cal(const string & s)
{
	string postfix(infix2postfix(s));
	cout << " 后缀表达式为:";
	cout << postfix << endl;
	cout << " ---------------------------------------" << endl;
	vector<int> numStack; // 用来存放数字的栈
	int letfNum = 0; // 用来存放左操作数
	int rightNum = 0; // 用来存放右操作数
	int result = 0; // 用来存放结果
	istringstream in(postfix);// 将后缀表达式变成流,然后通过每次读取一个字符来构成数字或者操作符
	string tmpNum="";
	char tmp;
	while ((tmp = in.get()) != -1) // 调试的时候发现,如果直接用in>>tmp,会吃掉中间的空格,
									// 因此改用get函数,当结束时,返回-1(调试的时候发现的)
	{
		if (tmp >= '0' && tmp <= '9') // 如果是数字,则不断的累积在tmpNum
		{
			tmpNum += tmp;
		}
		else if (tmp == ' ') // 如果为空格,则有两种情况。							
		{
			// 第一种情况是空格在数字后面(说明此时tmpNum不为空串)
			// 则读取出这个数字,放入栈中
			// 清空tmpNum内容
			if (!tmpNum.empty())
			{
				istringstream ss(tmpNum);
				int castedNum;
				ss >> castedNum;
				// 以上为C++中一种string转int的方法

				numStack.push_back(castedNum);
				tmpNum.clear();
			}
			else // 第二种情况,此时没有数字需要读取
				continue;

		}
		else if (tmp == '*' ||	tmp == '+' ) // 根据相应的符号进行计算
		{
			letfNum = numStack.back();
			numStack.pop_back();
			rightNum = numStack.back();
			numStack.pop_back();
			if (tmp == '*')
				result = letfNum * rightNum;
			else 
				result = letfNum + rightNum;
			numStack.push_back(result);
		}
		else
		{
			errQuit("计算过程中出现错误!");
		}
	} // while结束,说明此时获得的后缀表达式读取完成,这个时候result的值就是最终的结果了
	
	return result;
}

const string infix2postfix(const string &s)
{
	string result(""); // 输出结果
	vector<char> stackOp; // 存放操作符的栈

	// 用于判断左括号出现的位置是否适宜
	// 左括号出现必须前面是操作符,或者栈空的情况
	// 因此在输入操作符的时候,afterOp 为真
	// 输入数字后,afterOp为假
	bool afterOp = false; 

	// 用于判断除了左括号以外的其他符号的位置
	// 只有在数字后面才能添加其他操作符
	// 在数字后面则为真,否则为假
	bool afterNum = false; 

	// 用于判断是否在右括号后面,右括号后面必须为操作符
	// 在有括号后面则为真,否则为假
	bool afterRightBracket = false; 

	istringstream in(s); // 把输入的string按流的形式处理

	char tmp; // 用于临时存放输入的字符
	while (in >> tmp)
	{
		if ((tmp >= '0' && tmp <= '9')) // 判断是否为数字,如果为数字,就直接放在输出
		{
			if (afterRightBracket) errQuit("右括号后必须为操作符,不能为数字!");

			// 设置相应的状态变量
			afterOp = false;
			afterNum = true;
			afterRightBracket = false;

			result += tmp; // 输出结果到变量
			
		} // 处理数字的部分结束,循环进入下一次,开始其他输入(包括正确的符号和非法的符号)
		else // 处理符号
		{
			if (tmp == ')') 
			{
				if (stackOp.empty())
					errQuit("表达式不完整!"); // 防止用户输入(类似于): 1)
			}
			if ( tmp == '+'  || tmp == '*' )
			{
				if (result.empty()) // 如果是符号,但是结果的长度为空,说明还没有输入操作符就输入操作数了,非法!
					errQuit("输入错误!数据在操作符之前输入了"); // 防止用户输入(类似于): +1
			}
			if (stackOp.empty() && (tmp == '(' || tmp == '+'  || tmp == '*' ) ) //处理符号栈空的情况
			{		
				if (tmp == '(')
				{
					if (!afterOp && !result.empty())
						errQuit("左括号输入位置出错!");// 防止用户输入(类似于): 1(2+3)
				}
				stackOp.push_back(tmp);
				result += ' '; // 在输出中补充空白,以便分辨不同的数字和操作符

				// 设置相应的状态变量
				afterOp = true;	
				afterNum = false;
				afterRightBracket = false;
				continue;
				
			}
			// 以上三个if是对特殊初始情况的判断


			// 判断 每个不同的符号,进行相应的操作
			if (tmp == '(')
			{
				if (!afterOp) errQuit("左括号输入位置出错!");
				stackOp.push_back(tmp);

				// 设置相应的状态变量
				afterOp = true;
				afterNum = false;
				afterRightBracket = false;
			}
			else if (tmp == ')')
			{
				if (!afterNum && !afterRightBracket) errQuit("右括号输入位置出错!");
				
				char pop = stackOp.back();
				while ( pop != '(') // 获得右括号,在左括号之前的操作符都要全部退栈
				{
					stackOp.pop_back();
					result += ' ';
					result += pop;
					if (stackOp.empty()) break;
					pop = stackOp.back();
						
				}
				if (pop == '(') // 如果跳出循环是因为左括号,则正确,左括号退栈,否则括号不匹配
				{
					stackOp.pop_back();

					// 设置相应的状态变量
					afterOp = true;
					afterNum = false;
					afterRightBracket = true;
				}
				else
					errQuit("括号不匹配,缺少左括号");
			}
			else if (tmp == '*')  // 处理输入为乘号的情况
			{ 
				if (!afterNum && !afterRightBracket)  errQuit("乘号* 位置错误!");//乘号必须出现在数字或右括号之后
				char pop = stackOp.back();
				if (pop == '*') //因为乘号在这里是优先级最高的,因此栈内只可能出现一次乘号,所以用if
				{
					stackOp.pop_back();
					result += ' ';
					result += pop;
					result += ' ';
				}
				else // 如果不是乘号,但是给结果放入一个空格,从而分开各个数
				{
					result += ' ';
				}
				stackOp.push_back(tmp); // 把这次的操作符放入栈内

				// 设置相应的状态变量
				afterOp = true;
				afterNum = false;
				afterRightBracket = false;
			}
			else if (tmp == '+')
			{
				if (!afterNum && !afterRightBracket) errQuit("加号+ 位置错误!"); //加号必须出现在数字或右括号之后
				char pop = stackOp.back();
				while (pop == '*' || pop == '+')
				{
					stackOp.pop_back();
					result += ' ';
					result += pop;
					result += ' ';
					if (stackOp.empty()) break;
					pop = stackOp.back();
				}
				if (pop == '(')
				{
					result += ' ';
				}
				stackOp.push_back(tmp);
				// 设置相应的状态变量
				afterOp = true;
				afterNum = false;
				afterRightBracket = false;
			}
			else // 剩下的其他输入都为非法		
				errQuit("输入错误!");	
		} // 处理符号的部分结束
			
	} // while 结束

	// 弹出符号栈中剩余的操作符到输出变量中
	while (!stackOp.empty())
	{	
		char pop = stackOp.back();
		if (pop == '(') errQuit("括号不匹配,缺少右括号");
		else
		{
			result += ' ';
			result += stackOp.back();
			stackOp.pop_back();
		}
	}

	return result;
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值