关于字符串表达式求值

本文介绍了一种通过逆波兰表达式(后缀表达式)解析和计算字符串表达式的方法,旨在锻炼逻辑思维能力。文章详细阐述了解决方案的实现过程,包括如何判断运算符优先级、转换表达式为逆波兰表达式以及最终的计算步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

由于自身思维不够活跃,思考问题逻辑不够清晰,所以小弟的师傅给小弟我布置了个作业,字符串表达式求值,以此希望达到锻炼我思维逻辑能力的目的。
历时14天,完成作业,相关知识以及技术并不高深,目的在于锻炼逻辑思维能力。在此也想跟有相关需要的同学们分享下解题思路,有不足之处也希望大家不吝赐教,指点出来。谢谢。

解决该问题时首先要解决判断运算符优先级问题,后来了解到后缀表达式(即逆波兰表达式)后,决定先将表达式分解成逆波兰表达式 ,然后再根据每个运算符取出数字进行相应的运算。计算到最后即为表达式的值
涉及string字符串、动态数组vector、逆波兰表达式(网上由相应的解析,书上并没有出现),迭代器(主要起取出以及作转换范围使用)的相关知识。

低配版(仅支持0-9的正整数计算,可以计算的运算符包含+ - * / % ^ & |)
代码如下:`//原型: double Exper(const char * expr);
//使用: double retval = Expr(“1*2+(10/4)-3^1234”);
#include
#include
#include
#include
#include
#include
using namespace std;

bool gettruefalse(char a) //判断当前符号是否为符号
{
if (a == ‘+’ || a == ‘-’ || a == ‘*’ || a == ‘/’ || a == ‘%’ ||
a == ‘^’ || a == ‘|’ || a == ‘&’ || a == ‘(’ || a == ‘)’)
return true;
else
return false;
}

int getpriority(char c)
{
int temp;
if (c == ‘(’)
temp = 6;
else if (c == ‘*’ || c == ‘/’ || c == ‘%’)
temp = 5;
else if (c == ‘+’ || c == ‘-’)
temp = 4;
else if (c == ‘&’)
temp = 3;
else if (c == ‘^’)
temp = 1;
else if (c == ‘|’)
temp = 1;
else if (c == ‘)’) //如果为’)’,则一直将符号取出放入新字符串,直到遇到’(’(6)
temp = 0;
return temp;
}

string getnbl(string str)
{
string ok;//用于存放转换成后缀表达式的容器
vectorflag;//用于存放转换时的符号容器

while (str.length() != 0)//将旧字符串转换完成前,无限循环
{
	for (int a = 0; a < str.length(); a++) //遍历字符串
	{
		if (gettruefalse(str[a]))  //如果当前字符为符号
		{
			//如果符号容器为空,或者当前符号优先级大于符号容器优先级,或者符号容器最后一个符号为(,压入
			if (flag.size() == 0 || (getpriority(str[a]) > getpriority(*(flag.end() - 1))) || getpriority(*(flag.end() - 1)) == 6)
			{
				flag.push_back(str[a]);//将当前符号放入符号容器
				str.erase(str.begin(), str.begin() + 1); //截断旧字符串中的符号
				break;//跳出for循环,重新遍历
			}
			else if (getpriority(str[a]) == 0) //是)吗
			{
				str.erase(str.begin(), str.begin() + 1);//删掉旧字符串的),并将符号容器的符号移到新字符串,直到遇到(
				while (getpriority(*(flag.end() - 1)) != 6)//遇到(之前  无限弹出容器里的符号
				{
					ok += *(flag.end() - 1);//将符号容器的符号添加到新字符
					flag.erase(flag.end() - 1);//将刚被添加到新字符的字符删去
				}
				if ((getpriority(*(flag.end() - 1)) == 6))//将(去掉
				{
					flag.erase(flag.end() - 1);
					break;//跳到开头while循环处
				}
			}
			else if (getpriority(str[a]) <= getpriority(*(flag.end() - 1)))  //当前符号优先级小于或等于符号容器最后一个符号
			{
				ok += *(flag.end() - 1);//将符号容器的符号添加到新字符
				flag.erase(flag.end() - 1);//将刚被添加到新字符的字符删去
				break;
			}
		}
		else  //如果当前字符不为符号(即为数字或小数点)
		{
			ok += str[a];//将该数字转入新的字符串
			str.erase(a, 1); //在旧字符串中删除该数字
			break;//跳出for循环,重新开始遍历
		}
	}
}
//旧字符串清空后,将临时存放点的字符串依次取出放入新字符中
while (flag.size() != 0)
{
	ok += *(flag.end() - 1);
	flag.erase(flag.end() - 1);
}
return ok;

}

int jisuan(int a, char b, int c) //根据符号取数字进行相应的计算
{
int num = 0;//计算结果
if (b == ‘+’)
num = a + c;
else if (b == ‘-’)
num = a - c;
else if (b == ‘*’)
num = a * c;
else if (b == ‘/’)
num = a / c;
else if (b == ‘%’)
num = a % c;
else if (b == ‘^’)
num = a ^ c;
else if (b == ‘&’)
num = a & c;
else if (b == ‘|’)
num = a | c;
else if (b == ‘M’)
num = a && c;
else if (b == ‘N’)
num = a || c;
return num;
}

int getcount(string nbl)
{
vectornums;
int a = 0;
int answer; //存放结果
string zhuanhuan;
while (a < nbl.size())
{
if (gettruefalse(nbl[a]))//如果为符号
{ //从数字容器中去除倒数第二的数作为A,最后一个数作为B,执行该符号的运算
answer = jisuan(*(nums.end() - 2), nbl[a], *(nums.end() - 1));
nums.erase(nums.end() - 2, nums.end());
nums.push_back(answer);
a++;//遍历下一个字符
}
else //数字时 压人数字容器
{
zhuanhuan += nbl[a];
answer = atof(zhuanhuan.c_str());
nums.push_back(answer);
zhuanhuan.clear();
a++;
}
}
return *(nums.end() - 1); //容器最后一位数即为结果
}

int main()
{
char ch[100];
cout << “输入你要计算的字符串表达式(仅支持0-9的正整数运算):” << endl;
gets_s(ch);
cout << “字符串的原内容为:” << ch << endl;

string str(ch);//用于进行转换操作的字符串
string nbl;//用于存放转换成后缀表达式的容器
nbl = getnbl(str);//调用函数  将字符串转为逆波兰字符串
cout << "转为逆波兰表达式后:" << nbl << endl;
int num = getcount(nbl);
cout << "字符串:" << ch << "的结果为:" << num << endl;

system("pause");
fflush(stdin);
return 0;

}`

在这里插入图片描述

完全版(此时可以计算正负的整数(不支持小数点),运算符也包含+ - * / % & && ^ | ||):

//原型: double Exper(const char * expr);
//使用: double retval = Expr("1*2+(10/4)-3^1234");
#include<iostream>
#include<string>
#include<vector>
#include<cstdio>
using namespace std;

int gettruefalse(char a)  //判断当前符号是否为符号
{
	if (a == '1' || a == '2' || a == '3' || a == '4' || a == '5' || \
		a == '6' || a == '7' || a == '8' || a == '9' || a == '0')
		return 2;
	else if (a == '+' || a == '-' || a == '*' || a == '/' || a == '%' || \
		a == '^' || a == '|' || a == '&' || a == '(' || a == ')' || a == 'M' || a == 'N')
		return 1;
	else
		return 0;
}

int getpriority(char c)  //优先级排列
{
	int temp;
	if (c == '(')
		temp = 8;
	else if (c == '*' || c == '/' || c == '%')
		temp = 7;
	else if (c == '+' || c == '-')
		temp = 6;
	else if (c == '&')
		temp = 5;
	else if (c == '^')
		temp = 4;
	else if (c == '|')
		temp = 3;
	else if (c == 'M')  //等同于&&
		temp = 2;
	else if (c == 'N')  //等同于||
		temp = 1;
	else if (c == ')') //如果为')',则一直将符号取出放入新字符串,直到遇到'('(6)
		temp = 0;
	return temp;
}

void getvariant(string &str)
{
	int a = 0;//用于遍历
	for (a = 0; a < str.size(); a++) //将原字符串中的-(符号)用相应的标识符替换
	{
		if ((a == 0) && (str[a] == '-'))
		{
			str[a] = '.';
		}
		else if ((str[a] == '-') && (gettruefalse(str[a - 1]) == 1) && (str[a - 1] != ')')) //找到-并确认当前是否为符号
		{
			str[a] = '.';
		}
	}
	for (a = 0; a < str.size(); a++)  //将原字符串中的||、&&用相应的标识符替代
	{
		if (str[a] == '|' && str[a + 1] == '|')
		{
			str[a + 1] = 'N';
			str.erase(a, 1);
		}
		if (str[a] == '&' && str[a + 1] == '&')
		{
			str[a + 1] = 'M';
			str.erase(a, 1);
		}
	}
}

string getnbl(string str)
{
	getvariant(str);//调用函数用标记符替换字符串中的||、&&、-(负号)
	string ok;//用于存放转换成后缀表达式的容器
	vector<char>flag;//用于存放转换时的符号容器
	int a = 0;//用于作为遍历字符串的下标
	int b = a;//!b作为开始遍历数字的起始点

	while (a <str.length())//遍历整个字符串
	{
		if ((gettruefalse(str[a])) == 1)//当前字符为符号
		{
			//符号容器为空或者当前符号优先级大于容器末尾符号优先级,或当前容器末尾为(,压入容器
			if (flag.size() == 0 || (getpriority(str[a]) > getpriority(*(flag.end() - 1))) \
				|| *(flag.end() - 1) == '(')
			{
				flag.push_back(str[a]);
				a++;
			}
			else if (str[a] == ')') //如果为) 不压入,弹出容器符号  直到遇到(
			{
				while (*(flag.end() - 1) != '(')//当前符号不为(,则一直弹出容器符号
				{
					ok += *(flag.end() - 1);
					ok += ' ';              //每个符号以空格作为间隔
					flag.erase(flag.end() - 1);
				}
				flag.erase(flag.end() - 1);//删掉容器中的(
				a++;//跳过')'符号
			}
			else if (getpriority(str[a]) <= getpriority(*(flag.end() - 1)))//当前符号优先级小于或等于符号容器最后一个符号
			{
				ok += *(flag.end() - 1);//将符号容器的符号添加到新字符
				ok += ' ';
				flag.erase(flag.end() - 1);//将刚被添加到新字符的字符删去
			}	
		}
		else  //不为符号,则遍历字符串找到符号
		{
			b = a; //记录当前开始遍历的位置
			while(a<str.size())
			{
				if ( (gettruefalse(str[a]) == 1))  //当当前字符为符号
				{
					ok.append(str.begin() + b, str.begin() + a);//将遍历的起始位置到发现符号的位置的所有字符拼接到新的字符串,并加上空格作为间隔
					ok += ' ';
					break;  //跳出for循环 重新进行判断
				}
				else if ((a==(str.size()-1)) && (gettruefalse(str[a]) == 2)) //已经遍历到最后一个字符了 且最后一个字符为数字
				{
					a++;
					ok.append(str.begin() + b, str.begin() + a);//将遍历的起始位置到发现符号的位置的所有字符拼接到新的字符串,并加上空格作为间隔
					ok += ' ';
					break;  //跳出for循环 重新进行判断
				}
				a++;
			}
		}
	}
	while (flag.size() != 0) //遍历完原字符串后,将符号容器的符号全部弹出
	{
		ok += *(flag.end() - 1);
		ok += ' ';              //每个符号以空格作为间隔
		flag.erase(flag.end() - 1);
	}
	return ok;
}

int getanswer(int a, char b, int c)
{
	int num = 0;//计算结果
	if (b == '+') 
		num = a + c;
	else if (b == '-')
		num = a - c;
	else if (b == '*')
		num = a * c;
	else if (b == '/')
		num = a / c;
	else if (b == '%')
		num = a % c;
	else if (b == '^')
		num = a ^ c;
	else if (b == '&')
		num = a & c;
	else if (b == '|')
		num = a | c;
	else if (b == 'M')
		num = a && c;
	else if (b == 'N')
		num = a || c;
	return num;

}

int getcount(string nbl)  //计算逆波兰表达式的
{
	int num=0;//结果
	string zhuanhuan;
	vector<int> nums;//数字容器
	int a = 0;//遍历字符串下标
	int b = a;//记录开始遍历时的下标
	while (a < nbl.size())
	{
		if (gettruefalse(nbl[a])==1) //当前为符号
		{
			if (((nbl[a] == '/') || (nbl[a] == '%')) && (*(nums.end() - 1) == 0)) //计算除法和求余时,如果被除数为0,报错
			{
				cout << "除数不能为0,程序退出" << endl;
				system("pause");
				exit(-1);
			}
			
			num = getanswer(*(nums.end() - 2), nbl[a], *(nums.end() - 1));//弹出数字容器的2个数字 并根据符号去计算这2个数字
			nums.erase(nums.end() - 2, nums.end());//计算后去掉这2个数字,并将结果重新放入容器中
			nums.push_back(num);
			a++;//遍历下一个字符
		}
		else if (nbl[a] == ' ')
		{
			a++;
		}
		else
		{
			b = a;//记录开始找空格的位置
			while (a<nbl.size())
			{
				if (nbl[a] == ' ')//找到空格
				{
					if (nbl[b] == '.') //如果该数为负数(起始处为.)
					{
						nbl[b] = '-'; //将小数点转换为负号
					}
					zhuanhuan.append(nbl.begin() + b, nbl.begin() + a);
					num = atof(zhuanhuan.c_str());  //将该字符串转换为数字 并压入数字容器中
					zhuanhuan.clear();
					nums.push_back(num);
					break;
				}
				a++;
			}

		}
	}
	num = *(nums.end() - 1);
	return num;
}

int main()
{
	char ch[100];
	cout << "输入你要计算的字符串表达式(仅支持整数运算):" << endl;
	gets_s(ch);
	string str(ch);//用于进行转换操作的字符串
	int s = 0;
	while (s < str.size())//检查用户输入表达式是否正确
	{
		if (gettruefalse(str[s]) == 0)
		{
			cout << "字符串表达式输入错误" << endl;
			system("pause");
			return 0;
		}
		s++;
	}
	string nbl;//用于存放转换成后缀表达式的容器
	nbl = getnbl(str);//调用函数  将字符串转为逆波兰字符串
	int num;//表达式结果
	num = getcount(nbl);	
	cout << "转为逆波兰表达式后:" << nbl << ",.(-)、M(&&)、N(|| )为标记符"<<endl;
	cout << "字符串表达式:" << ch << "的结果为:" << num << endl;
	cout << "结果为:" << num << endl;
	system("pause");
	fflush(stdin);
	return 0;
}

运行结果:在这里插入图片描述在这里插入图片描述在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值