算术表达式求值

算术表达式求值

问题描述:编写程序,计算算术表达式串的值,具体要求如下:

① 表达式串在运行时输入。
② 表达式串支持+、-、、/(精确除) 、%(整除取余)、圆括号等运算符,且支持任意位数的整 形常量和浮点型常量。如“33/2-(41.23+2)(52%7)”的值为“-113.19”。
③ 运算符优先级依次为:括号、乘除、加减,若优先级相同,则从左至右。
④ 当表达式串非法时,能提示信息。 涉及算法及知识:栈的应用、任意位数的整数/浮点数字符串转换为对应的整数/浮点数。

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<math.h>
#define MaxSize 100 

typedef struct CharStack				//字符栈 
{
	char data[MaxSize];
	int top;
}cStack;

typedef struct DoubleStack				//数据栈 
{
	double data[MaxSize];
	int top;
}dStack;


//字符栈初始化 
void Initc(cStack* s1)
{
	s1->top = -1;
}

//字符进栈 
int Pushc(cStack* c1, char op)
{
	if (c1->top < MaxSize)
	{
		c1->data[++c1->top] = op;
		return 1;
	}
	else return 0;
}

//寻找栈顶元素 
char Gettopc(cStack* c1)
{
	return c1->data[c1->top];
}

//字符出栈 
char Popc(cStack* c1)
{
	return c1->data[c1->top--];
}

//初始化数据栈 
void Initd(dStack* d1)
{
	d1->top = -1;
}

//数据进栈 
int Pushd(dStack* d1, double data)
{
	if (d1->top < MaxSize)
	{
		d1->data[++d1->top] = data;
		return 1;
	}
	else return 0;
}

//数据出栈 
double Popd(dStack* d1)
{
	return d1->data[d1->top--];
}

//当前扫描运算符优先级
int Inthis(char op)
{
	switch (op)
	{
	case '(': return 6;
	case '+': case '-': return 2;
	case '*': case '/':case '%': return 4;
	}
}

//栈顶扫描运算符优先级
int Intop(char op)
{
	switch (op)
	{
	case '(': return 1;
	case '+': case '-': return 3;
	case '*': case '/':case '%': return 5;
	}
}

//转化字符串
void Trans(char* s1, char* s2)
{
	int i = 0;
	int j = 0;
	int f1 = -1;
	//f1为0表示上次输出为数字,f1为1表示上次输出为字符
	int f2 = -1;
	//f2为0表示上次扫描为数字,f2为1表示上次扫描为运算符,用于区分数字后加空格
		cStack string;
	Initc(&string);

	while (s1[i] != '\0')
		//处理负数 
	{
		if (s1[0] == '-')
			//第一位数字为负数时
		{
			j = strlen(s1);
			while (j > 0)
			{
				s1[j + 5] = s1[j];
				j--;
			}
			s1[j++] = '(';
			s1[j++] = '0';
			s1[j++] = '-';
			s1[j++] = '1';
			s1[j++] = ')';
			s1[j] = '*';

		}
		if (s1[i] == '(' && s1[i + 1] == '-')
			//非第一位负数时 
		{
			j = strlen(s1);
			while (j > i + 1)
			{
				s1[j + 5] = s1[j];
				j--;
			}
			s1[j++] = '(';
			s1[j++] = '0';
			s1[j++] = '-';
			s1[j++] = '1';
			s1[j++] = ')';
			s1[j] = '*';
			i = i + 5;
		}
		i++;
	}

	i = 0;
	j = 0;
	while (s1[i] != '\0')
	{
		if (f1 == 0 && f2 == 1)
			//若上次的输出为数字,上次循环扫描为字符,则表示该数字串结束,则在数字后加空格区分 
		{
			s2[j++] = ' ';
			f1 = 1;
		}
		if ((s1[i] >= '0' && s1[i] <= '9') || s1[i] == '.')
		{
			s2[j++] = s1[i];
			f2 = 0;
			f1 = 0;
		}
		else if (s1[i] == '+' || s1[i] == '-' || s1[i] == '*' || s1[i] == '/' || s1[i] == '%' || s1[i] == '(')
		{
			f2 = 1;
			if (string.top<0 || Inthis(s1[i])>Intop(Gettopc(&string)))
			{
				Pushc(&string, s1[i]);
			}
			else
			{
				while (string.top >= 0 && Inthis(s1[i]) < Intop(Gettopc(&string)))
					//当前扫描字符优先级不断与栈顶字符优先级比较,当前字符小于栈顶字符时退栈并输出 
				{
					s2[j++] = Popc(&string);
					f1 = 1;
				}
				if (string.top<0 || Inthis(s1[i])>Intop(Gettopc(&string)))
					//当前字符优先级大于栈顶优先级或栈空时当前字符压入字符栈内 
				{
					Pushc(&string, s1[i]);
				}

			}
		}
		else if (s1[i] == ')')
		{
			f2 = 1;
			if (Gettopc(&string) != '(')
				//若括号仅包含数字则没有输出运算符
			{
				f1 = 1;
			}
			while (Gettopc(&string) != '(')
			{
				s2[j++] = Popc(&string);
			}
			Popc(&string);
		}
		i++;
	}
	while (string.top >= 0)
		//将栈内剩余的运算符依次退栈输出 
	{
		s2[j++] = Popc(&string);
	}
	s2[j] = '\0';
}

void check(char* s2, int pd)
{
	int i, j;
	i = 1;
	j = 0;
	while (s2[i] != '\0')
	{
		if (s2[i] == '(')
		{
			if (s2[i - 1] != '+' || s2[i - 1] != '-' || s2[i - 1] != '*' || s2[i - 1] != '/' || s2[i - 1] != '%' || s2[i - 1] != '(')
			{
				printf("error!表达式格式出错!\n");
				pd = 0;
				break;
			}
		}
		i++;
	}
	while (s2[j] != '\0')
	{
		if (s2[j] == ')')
		{
			if (s2[j + 1] != '+' || s2[j + 1] != '-' || s2[j + 1] != '*' || s2[j + 1] != '/' || s2[j + 1] != '%' || s2[j + 1] != ')')
			{
				printf("error表达式格式错误!\n");
				pd = 0;
				break;
			}
		}
		j++;
	}
}


//表达式求值 
double Calculate(char* s2, double* pd2)
{
	int i = 0;
	int f;
	double data1, data2;
	double sum;
	double exo;
	dStack ds1;
	Initd(&ds1);
	while (s2[i] != '\0')
	{
		if (s2[i] == '+' || s2[i] == '-' || s2[i] == '*' || s2[i] == '/' || s2[i] == '%')
			//若为运算符获取栈顶两个元素进行计算 
		{
			data1 = Popd(&ds1);
			data2 = Popd(&ds1);
			if (s2[i] == '+') Pushd(&ds1, data2 + data1);
			else if (s2[i] == '-') Pushd(&ds1, data2 - data1);
			else if (s2[i] == '*') Pushd(&ds1, data2 * data1);
			else if (s2[i] == '/')
			{
				if (data1 == 0)
				{
					printf("\nerror!分母不能为零!\n");
					*pd2 = 0;
				}
				else
					Pushd(&ds1, data2 / data1);
			}
			else if (s2[i] == '%')
			{
				if (data1 == 0)
				{
					printf("\nerror!分母不能为零!\n");
					*pd2 = 0;
				}
				else if (fmod(data2, 1) != 0)
				{
					printf("\nerror!整除求余不能出现小数!\n");
					*pd2 = 0;
				}
				else if (fmod(data1, 1) != 0)
				{
					printf("\nerror!整除求余不能出现小数!\n");
					*pd2 = 0;
				}
				else
					Pushd(&ds1, fmod(data2, data1));
			}

		}
		else
		{
			f = 0;
			//初始化为0为整数部分标记,1为小数部分标记 //初始化为0为整数部分标记,1为小数部分标记 
			sum = 0;
			exo = 1;
			while (s2[i] != ' ' && s2[i] != '+' && s2[i] != '-' && s2[i] != '*' && s2[i] != '/' && s2[i] != '%')
			{
				if (s2[i] == '.')
					//若有小数点,进入小数转化模式//若有小数点,进入小数转化模式 
				{
					f = 1;
					i++;
					continue;
				}
				if (f == 0)
				{
					sum = sum * 10 + (double)(s2[i] - '0');
				}
				else
				{
					exo = exo * 10;
					sum = sum + ((double)(s2[i] - '0')) / exo;
				}
				i++;
			}
			if (s2[i] == '+' || s2[i] == '-' || s2[i] == '*' || s2[i] == '/' || s2[i] == '%') i--;
			//转化成功一个数据,若下个字符为运算符,则i--,回到当前运算的数据位置 
			Pushd(&ds1, sum);
		}
		i++;
	}
	return Popd(&ds1);
}


void main()
{
	
	double end;
	char s1[MaxSize];
	//用于存储前缀表达式 
	char s2[MaxSize];
	//用于存储后缀表达式 
	printf("*******请输入表达式*******\n\n");
	while (1)
	{
		int pd1 = 1;
		double* pd2 = 1;
		scanf("%s", s1);
		Trans(s1, s2);
		//处理字符串,并转化为后缀表达式,存放在s2中 
		check(s2, pd1);

		end = Calculate(s2, &pd2);
		//后缀表达式求值//后缀表达式求值
		if (pd1 == 0 || pd2 == 0)
			printf("\n");
		else if (pd1 == 1 || pd2 == 1)
			printf("\n计算结果为: %.6f(结果保留六位小数)\n\n", end);
	}
}
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值