【C/C++ 08】简单计算器

一、题目

输入算术表达式,可包含空格,检查算术表达式的合法性,若算术表达式不合法,打印错误类型,若合法,则进行运算,打印计算结果。

二、算法

  1. 将输入的算术表达式字符串去除空格。

  2. 检查输入的算术表达式的合法性,判断算式首字符是否合法(只能是前括号或正负号),判断括号是否匹配,判断连续字符是否合法(运算符后面可跟左括号,右括号后面可跟运算符,左括号后面可跟加减号表正负,左右括号之间不能直接相连,数字后面不能直接跟左括号)。

  3. 构造符号栈和数据栈对合法的算术表达式进行计算,计算的过程就是从数据栈取出两个数据和符号栈取出一个数据,然后计算得到一个结果,再将结果压入数据栈。

三、代码

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 1

#include <iostream>
#include <vector>
#include <stack>
using namespace std;

vector<char> g_opt = { '+', '-', '*', '/'};

void RemoveSpaces(char* input)
{
	for (int i = 0; i < strlen(input); ++i)
	{
		if (input[i] == ' ')
		{
			for (int j = i; j < strlen(input); ++j)
			{
				input[j] = input[j + 1];
			}
			--i;
		}
	}
}

bool CheckInput(char* input)
{
	stack<char> bracket;	// 用于判断括号的合法性

	for (int i = 0; i < strlen(input); ++i)
	{
		if (!isdigit(input[i]))
		{
			if (find(g_opt.begin(), g_opt.end(), input[i]) == g_opt.end()
				&& input[i] != '(' && input[i] != ')')
			{
				cout << "存在非法字符 " << input[i];
				return false;
			}

			if (input[i] == '(')
			{
				bracket.push(input[i]);
			}
			else if (input[i] == ')')
			{
				if (bracket.empty())
				{
					cout << "括号不匹配";
					return false;
				}
				else
				{
					bracket.pop();
				}
			}

			// 第一个字符必须是数字或正负号或左括号
			if (i == 0 && input[i] != '+' && input[i] != '-' && input[i] != '(')
			{
				cout << "首字符错误";
				return false;
			}

			if (!isdigit(input[i + 1]))
			{
				if (input[i] == '(')
				{
					// 左括号后面可以跟加减表示正负
					if (input[i + 1] == '*' || input[i + 1] == '/' || input[i + 1] == ')')
					{
						cout << "出现连续符号 " << input[i + 1];
						return false;
					}
				}
				else if (input[i] == ')')
				{
					// 有括号后面不能直接跟前括号,可以跟操作符
					if (input[i + 1] == '(')
					{
						cout << "出现连续符号 " << input[i + 1];
						return false;
					}
				}
				else
				{
					// 操作符后面可以跟左括号
					if (input[i + 1] != '(')
					{
						cout << "出现连续符号 " << input[i + 1];
						return false;
					}
				}
			}
		}
		else
		{
			// 数字后面不能直接跟左括号
			if (input[i + 1] == '(')
			{
				cout << "括号位置错误 " << input[i + 1];
				return false;
			}
		}
	}

	if (!bracket.empty())
	{
		cout << "括号不匹配";
		return false;
	}

	return true;
}

bool PopCal(stack<int>& datas, stack<char>& symbols)
{
	if (datas.size() < 2 || symbols.empty())
		return false;

	if (symbols.top() == '(')
	{
		symbols.pop();
		return false;
	}

	int x = datas.top();
	datas.pop();
	int y = datas.top();
	datas.pop();
	char opt = symbols.top();
	symbols.pop();

	int res = (unsigned int)-1;
	if (opt == '+')
		res = x + y;
	else if (opt == '-')
		res = x - y;
	else if (opt == '*')
		res = x * y;
	else if (opt == '/')
		res = x / y;
	datas.push(res);
	return true;
}

int Calculate(char* input)
{
	// 数据栈和符号栈
	stack<int> datas;
	stack<char> symbols;

	int flag = 1;	// 1表示正,-1表示负数
	for (int i = 0; i < strlen(input); ++i)
	{
		if (isdigit(input[i]))
		{
			// 将连续数字字符转为整型数据存入数据栈
			string digit = "";
			digit += input[i];
			while (isdigit(input[i + 1]))
			{
				++i;
				digit += input[i];
			}
			int num = atoi(digit.c_str());
			datas.push(num * flag);
			flag = 1;

			// 数据压栈后发现栈顶是乘除号,直接进行计算
			if (!symbols.empty() && (symbols.top() == '*' || symbols.top() == '/'))
				PopCal(datas, symbols);
		}
		else
		{
			// 左括号的优先级最高
			if (input[i] == '(')
			{
				symbols.push(input[i]);
				continue;
			}

			// 判断负号
			if (i == 0 || input[i - 1] == '(')
			{
				if (input[i] == '-')
					flag = -1;
				continue;
			}

			if (input[i] == '*' || input[i] == '/')
			{
				// 乘除符号直接压入符号栈
				symbols.push(input[i]);
			}
			else if (input[i] == '+' || input[i] == '-')
			{
				// 若符号栈存有加减号未计算,先计算再将加减号压栈
				if (!symbols.empty() && (symbols.top() == '+' || symbols.top() == '-'))
				{
					PopCal(datas, symbols);
				}
				symbols.push(input[i]);
			}
			else if (input[i] == ')')
			{
				if (symbols.top() == '(')
				{
					// 表示括号括住了数字,而不是表达式
					symbols.pop();
				}
				else
				{
					// 循环出栈计算,直到遇到左括号
					while (PopCal(datas, symbols))
					{}
				}
			}
		}
	}

	// 循环出栈计算
	while (PopCal(datas, symbols))
	{}
	
	return datas.top();
}

int main()
{
	while (1)
	{
		char input[128];
		cout << "请输入算术表达式:";
		fgets(input, sizeof(input), stdin);
		input[strlen(input) - 1] = 0;

		RemoveSpaces(input);
		if (strcmp(input, "exit") == 0)
			break;
		if (!CheckInput(input))
		{
			cout << ",表达式输入格式错误,请重新输入!" << endl;
			continue;
		}

		int res = Calculate(input);
		cout << input << " = " << res << endl;
	}

	return 0;
}

四、测试

相关推荐
XH华8 分钟前
初识C语言之二维数组(下)
c语言·算法
高山我梦口香糖1 小时前
[react]searchParams转普通对象
开发语言·前端·javascript
冷眼看人间恩怨1 小时前
【Qt笔记】QDockWidget控件详解
c++·笔记·qt·qdockwidget
信号处理学渣1 小时前
matlab画图,选择性显示legend标签
开发语言·matlab
红龙创客1 小时前
某狐畅游24校招-C++开发岗笔试(单选题)
开发语言·c++
Lenyiin1 小时前
第146场双周赛:统计符合条件长度为3的子数组数目、统计异或值为给定值的路径数目、判断网格图能否被切割成块、唯一中间众数子序列 Ⅰ
c++·算法·leetcode·周赛·lenyiin
jasmine s1 小时前
Pandas
开发语言·python
biomooc2 小时前
R 语言 | 绘图的文字格式(绘制上标、下标、斜体、文字标注等)
开发语言·r语言
骇客野人2 小时前
【JAVA】JAVA接口公共返回体ResponseData封装
java·开发语言
black^sugar2 小时前
纯前端实现更新检测
开发语言·前端·javascript