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

news2024/12/23 8:46:55

一、题目

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

二、算法

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

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

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

三、代码

#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;
}

四、测试

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1432040.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java并发(二十三)----同步模式之保护性暂停

1、定义 即 Guarded Suspension&#xff0c;用在一个线程等待另一个线程的执行结果 要点 有一个结果需要从一个线程传递到另一个线程&#xff0c;让他们关联同一个 GuardedObject 如果有结果不断从一个线程到另一个线程那么可以使用消息队列 JDK 中&#xff0c;join 的实现…

【MySQL】学习并使用DQL实现排序查询和分页查询

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-SP91zTA41FlGU0Ce {font-family:"trebuchet ms",verdana,arial,sans-serif;font-siz…

【Linux进程间通信】匿名管道

【Linux进程间通信】匿名管道 目录 【Linux进程间通信】匿名管道进程间通信介绍进程间通信目的进程间通信发展进程间通信分类 管道用fork来共享管道原理站在文件描述符角度——深度理解管道站在内核角度——管道本质 匿名管道在myshell中添加管道的实现&#xff1a;管道读写规则…

Linux 多线程 | 线程的概念

线程的概念 线程是一个执行分支&#xff0c;执行粒度比进程更细&#xff0c;调度成本更低&#xff1b; 线程是进程内部的一个执行流&#xff1b; 线程是CPU调度的基本单位&#xff0c;进程是承担分配系统资源的基本实体。 之前我们学习过虚拟地址空间的知识&#xff0c;知道…

大礼包 - 华为机试真题题解

考试平台&#xff1a; 时习知 分值&#xff1a; 200分&#xff08;第二题&#xff09; 考试时间&#xff1a; 2024-01-31 &#xff08;两小时&#xff09; 题目描述 某公司针对新用户推出大礼包&#xff0c;从任意一天注册开始&#xff0c;连续登陆 x 天&#xff0c;每天可以领…

C# 信号量(Semaphore)详细使用案例

文章目录 简介信号量的工作原理使用场景使用示例其他使用实例1. 数据库连接池管理2. 文件读写同步3. 生产者消费者问题4. 打印任务队列同步5. Web服务器并发请求限制 简介 在C#中&#xff0c;信号量&#xff08;Semaphore&#xff09;是.NET框架提供的一个同步类&#xff0c;位…

vue3:23—自定义hooks

正是因为有了hooks&#xff0c;组合式才发挥出了威力 其实 hooks 和 vue2 中的 mixin 有点类似&#xff0c;但是相对 mixins 而言&#xff0c; hooks 更清楚复用功能代码的来源, 更清晰易懂。 如何定义hooks 具备可复用功能&#xff0c;才需要抽离为 hooks 独立文件函数名/文…

【Linux】Ext2 文件系统

文件系统 前言一、磁盘硬件1. 磁盘的物理存储结构2. 磁盘存储的逻辑抽象结构 二、理解 Ext2 文件系统1. 初步理解文件系统2. 深入理解文件系统&#xff08;1&#xff09;inode Table&#xff08;2&#xff09;Data blocks&#xff08;3&#xff09;inode Bitmap&#xff08;4&a…

Lambda表达式(匿名函数)

C11中引入了lambda表达式&#xff0c;定义匿名的内联函数。 我们可以直接原地定义函数而不用再跑到外面去定义函数跳来跳去。 同时在stl的排序上也有作用。 [capture] (parameters) mutable ->return-type {statement}下面逐一介绍各个参数的含义. [capture] : 捕获&#…

【Spring】自定义注解 + AOP 记录用户的使用日志

目录 ​编辑 自定义注解 AOP 记录用户的使用日志 使用背景 落地实践 一&#xff1a;自定义注解 二&#xff1a;切面配置 三&#xff1a;Api层使用 使用效果 自定义注解 AOP 记录用户的使用日志 使用背景 &#xff08;1&#xff09;在学校项目中&#xff0c;安防平台…

阿里计算巢:开启数据集市场的宝库,助力AI研究和应用

阿里计算巢 阿里数据巢提供了一个丰富的数据集市场&#xff0c;官方地址&#xff1a; https://computenest.console.aliyun.com/dataset/service/cn-hangzhou 可以看到数据集内容涵盖了多个领域&#xff0c;且还在不断增加中。关键是免费&#xff01;且支持下载到本地。 以下…

oracle 根据身份证号码与指定日期计算年龄

自定义函数&#xff1a; CREATE OR REPLACE FUNCTION 获取年龄(身份证号 varchar2, 指定时间 date) RETURN varchar2 AS 年龄 varchar2(16); BEGINif length(身份证号) >18 thenSELECT TRUNC( MONTHS_BETWEEN(指定时间, TO_DATE(SUBSTR(身份证号, 7, 8), YYYYMMDD) …

Django学习记录01

1.项目结构 djangoProject02 ├── manage.py 【项目的管理&#xff0c;启动项目、创建app、数据管理】【不要动】【常常用】 └── jangoProject02 ├── __init__.py ├── settings.py 【项目配置】 【常常修改】 ├── urls.py …

Linux 查看系统信息 + 服务信息命令(简记)

概述 作用&#xff1a;Linux 运维工作中常用的命令速查 小步教程 (xiaobuteach.com) Linux 命令大全 | 菜鸟教程 (runoob.com) 文本编辑器vim 本章大纲 | 小步教程 vim 多文件编辑 | 小步教程 常用 ps 查看服务启动命令 Linux ps 命令 | 菜鸟教程 (runoob.com) # 查找…

Linux进程信号处理:深入理解与应用(2​​)

&#x1f3ac;慕斯主页&#xff1a;修仙—别有洞天 ♈️今日夜电波&#xff1a;its 6pm but I miss u already.—bbbluelee 0:01━━━━━━️&#x1f49f;──────── 3:18 &#x1f504; ◀️…

32ADC模数转换器&AD单通道&多通道

目录 一.简介 二.逐次逼近法​编辑 三.结构框图 四.小tips (1)转换模式 &#xff08;2&#xff09;触发控制 &#xff08;3&#xff09;数据对齐 &#xff08;4&#xff09;转换时间 &#xff08;5&#xff09;校准 &#xff08;6&#xff09;硬件电路 五.相关函数 …

Java语法学习IO流

Java语法学习IO流 大纲 文件IO流 具体案例 1. 文件 基本介绍 创建文件 第一种&#xff1a; public static void main(String[] args) {String filePathName "d:\\news1.txt";File file new File(filePathName);try {file.createNewFile();} catch (IOExceptio…

vulhub中Apache Druid 代码执行漏洞复现(CVE-2021-25646)

Apache Druid是一个开源的分布式数据存储。 Apache Druid包括执行嵌入在各种类型请求中的用户提供的JavaScript代码的能力。这个功能是为了在可信环境下使用&#xff0c;并且默认是禁用的。然而&#xff0c;在Druid 0.20.0及以前的版本中&#xff0c;攻击者可以通过发送一个恶…

2018 年全国职业院校技能大赛高职组“信息安全管理与评估”赛项任务书(笔记解析)

1. 网络拓扑图 2. IP 地址规划表 3. 设备初始化信息 阶段一 任务 1:网络平台搭建 1、根据网络拓扑图所示,按照 IP 地址参数表,对 WAF 的名称、各接口 IP 地址 进行配置。 2、根据网络拓扑图所示,按照 IP 地址参数表,对 DCRS 的名称、各接口 IP 地址 进行配置。 3、根据网…

C++项目 -- 高并发内存池(二)Thread Cache

C项目 – 高并发内存池&#xff08;二&#xff09;Thread Cache 文章目录 C项目 -- 高并发内存池&#xff08;二&#xff09;Thread Cache一、高并发内存池整体框架设计二、thread cache设计1.整体设计2.thread cache哈希桶映射规则3.TLS无锁访问4.thread cache代码 一、高并发…