表达式求值之字符串处理

分析:由于要求用C语言,我们用一个char类型的数组存表达式字符串,用一个int类型的二维数组存左括号和右括号的位置,然后直接对该表达式字符串按优先级进行处理,核心功能为:
  (1)对括号的深度遍历,并存入flag二维数组中的findKuohao();子函数。
在这里插入图片描述
  计算时优先处理最后面的括号,即表达式中最后面那组括号中最里面的那层。依次向前计算。
  (2)字符串中某段表达式求解后的值再返回字符串时,对该段后面字符串的移动处理moveStr(int left, int right, int numLen);子函数。
  (3)flag二维数组中括号的位置 跟随 字符串的移动 而变化的flagFolMoveStr();子函数。

PS:由于自己临时编写,算法的复杂度可能并不最优,欢迎大家交流优化,提一些意见,龙少感激不尽,代码如下:

创建一个priority.h头文件,代码如下:

#ifndef _PRIORITY_H_
#define _PRIORITY_H_

//求运算符优先级
int priority(double o)
{
	switch((int)o)
	{
		case 0: return 0;
		case 5: return 1;
		case 1:
		case 2: return 2;
		case 3:
		case 4: return 3;
	}
}

//判断是否为运算符 
int isOperator(char c)
{
	if(c == '+' || c == '-' || c == '*' || c == '/' || c == '(' || c == ')') return 1;
	else return 0;
}

//判断是否为数字
int isNum(char c)
{
	if(c >= '0' && c <= '9') return 1;
	else return 0;
}

#endif

创建一个main.c文件,代码如下:

/*
 * 重点难点:
 * 1. 负数的运算
 * 2. 括号的嵌套
 * 2.1 括号的多层嵌套
 * 2.2 同层中多个括号 
 */

#include <stdio.h>
#include <string.h>
#include "priority.h"
#include <stdlib.h>

char	s[512];				//总
int		iS = 0;				//s的下标 
int		flag[128][2];		//存括号的位置flag[iFlag][0]为第iFlag个括号的左括号,flag[iFlag][1]是右括号 
int		iFlag = 0;			//flag的顶部 
int		flagLength = -1;	//括号的个数,即flag的长度 
double	res;				//最后的结果
double	x, y;				//x 'o' y
char	o;
int		leftKH, rightKH;	//当前运算的左右括号

void	doCC();
void	doJJ();
void	flagFolMoveStr();
void	moveStr(int left, int right, int numLen);
void	doNum(double *num);
void	findKuohao();

//括号的位置跟随整体字符串的移动而改变
void flagFolMoveStr()
{
	int i = flagLength;
	int len1 = rightKH - leftKH;	//现在左右括号之间的长度
	int len2 = flag[i][1] - flag[i][0];	//原来左右括号之间的长度
	int len = len2 - len1;	//变化的长度,即字符串移动的距离
	
	if(len != 0)
		while(i >= 0)	//将存括号的数组内容,跟随当前移动后面字符串的操作,同步更改括号位置 
		{
			if(flag[i][1] > flag[flagLength][1])	//如果第i个右括号位置在当前右括号的后面,则更新该括号当前位置
				flag[i][1] -= len;	//每一个括号的位置都前移len 
			i--;
		}
		
	flag[flagLength][0] = 0, flag[flagLength][1] = 0;	//当前括号位置清零 
	flagLength--;										//括号个数减一 
}

//将字符串s[left...right]内容替换为num,要移动后面的内容
void moveStr(int left, int right, int numLen)
{
	int i = right;
	int len = right - left;
	int moveLen = left + numLen - right;

	if(len == numLen) return;	//如果删除的长度和要插入的数字长度相等,无需移动后面元素 
	else if(len > numLen)		//如果大于,后面元素前移 
	{
		while(s[i-1])
		{
			s[i+moveLen] = s[i];
			i++;
		}
		memset(s+i+moveLen, 0, -moveLen);	//前移之后,将后面使用过的空间清零 
	}
	else						//否则小于,后面元素后移 
	{
		while(s[i+1]) i++;		//i指向最后一位
		while(i >= right)		//后面元素后移 
		{
			s[i+moveLen] = s[i];
			i--;
		}
	}
	rightKH += moveLen;
}

//提取数字给num 
void doNum(double *num)
{
	char numTemp[32];				//字符串转double的临时字符串数组 
	int i;							//下标,索引,遍历介质 
	memset(numTemp, 0, sizeof(numTemp));
	i = 0;
	if(s[iS] == '-') numTemp[i++] = s[iS++];
	else if(!isNum(s[iS]))
	{
		puts("表达式有误!");
		exit(EOF);
	}
	while(isNum(s[iS]))
	{
		numTemp[i] = s[iS];
		i++, iS++;
	}
	if(s[iS] == '.')
	{
		numTemp[i++] = '.';
		iS++;
		while(isNum(s[iS]))	//小数部分 
		{
			numTemp[i] = s[iS];
			i++, iS++;
		}
	}
	*num = atof(numTemp);
}

//查找s中括号的位置,存入flag中 
void findKuohao()
{
	if(s[iS] == '(')	//如果遇到左括号,flag[iFlag][0]存左括号的位置,1存右括号的位置 
	{
		flag[iFlag][0] = iS;	//存左括号的位置
		iS++;
		while(s[iS] && s[iS] != ')')	//找右括号 
		{
			if(s[iS] == '(')	//如果又遇到左括号,递归调用该函数,将存到flag的下一个位置 
			{
				while(flag[iFlag][0] != -1)
				{
					iFlag++;
				}
				findKuohao();
				iFlag--;
				while(flag[iFlag][1] != -1)
					iFlag--;	//寻找完毕,iFlag回来继续找该层的右括号 
			}
			iS++;
		}
		if(!s[iS])	//如果字符串到尾部还没找到右括号,匹配失败 
		{
			printf("表达式中括号不匹配!\n");
			exit(EOF);
		}
		flag[iFlag][1] = iS;	//存入右括号
		flagLength++;
		while(flag[iFlag][1] != -1) 
			iFlag++;
	}
}

//做乘除运算 
void doCC()
{
	double res;
	char resTemp[16], *pr;
	int liftTemp, rightTemp;
	
	while(iS < rightKH)
	{
		if(s[iS] == '*' || s[iS] == '/')
		{
			pr = resTemp;
			memset(resTemp, 0, sizeof(resTemp));
			//将指针前移至运算符前的数字首地址 
			iS--;
			while(isNum(s[iS]) || s[iS] == '.' || s[iS] == '-' && !isNum(s[iS-1]) ) iS--;
			iS++;
			//记录表达式左端
			liftTemp = iS;
			//将运算符左面数字的字符串转为double赋值给x,同时指针指向数字的下一位
			doNum(&x);
			//数字的下一位即运算符,存在o中
			o = s[iS];
			//将指针指向运算符后面数字的首地址
			iS++;
			//将运算符右面数字的字符串转为double赋值给y,同时指针指向数字的下一位
			doNum(&y);
			//记录表达式的右端的下一位
			rightTemp = iS;
			switch(o)
			{
				case '*': res = x * y; break;
				case '/': res = x / y; break;
			}
			sprintf(resTemp, "%lf", res);
			moveStr(liftTemp, rightTemp,strlen(resTemp));
			while(*pr) s[liftTemp++] = *(pr++);
//			printf("doCC: %s——%s\n", resTemp, s);
			iS = liftTemp - 1;
		}
		iS++;
	}
}

//做加减运算 
void doJJ()
{
	double res;
	char resTemp[16], *pr;
	int leftTemp, rightTemp;
	
	while(iS < rightKH)
	{
		if(iS - 1 >= 0 && (s[iS] == '+' || s[iS] == '-'))
		{
			memset(resTemp, 0, sizeof(resTemp));
			pr = resTemp;
			iS--;
			while(isNum(s[iS]) || s[iS] == '.' || s[iS] == '-' && !isNum(s[iS-1])) iS--;
			iS++;
			leftTemp = iS;
			doNum(&x);
			o = s[iS];
			iS++;
			doNum(&y);
			rightTemp = iS;
			switch(o)
			{
				case '+': res = x + y; break;
				case '-': res = x - y; break;
			}
			sprintf(resTemp, "%lf", res);
			moveStr(leftTemp, rightTemp, strlen(resTemp));
			while(*pr) s[leftTemp++] = *(pr++);
//			printf("doJJ: %s——%s\n", resTemp, s);
			iS = leftTemp - 1;
		}
		iS++;
	}
}

int main()
{
	memset(flag, -1, sizeof(flag));
	memset(s, 0, sizeof(s));
	gets(s);
	
	//找括号的位置
	while(s[iS])
	{
		findKuohao();
		iS++;
	}
	
	int i;
	
	//先计算括号里的值
	while(flagLength > -1)
	{
//		for(i = 0; i <= flagLength; i++) printf("%d,, %d\n", flag[i][0], flag[i][1]);//(3-2+(6-5)/(5-4)/(4-2-1))
		//1 获取最后的最内层的括号的位置
		leftKH = flag[flagLength][0];
		rightKH = flag[flagLength][1];
		iS = leftKH;
		//2 从左括号开始计算
		//2.1 优先计算乘除
		doCC();
		iS = leftKH;
		//2.2 再计算加减
		doJJ();
		moveStr(leftKH, leftKH + 1, 0);	//删除左括号
		moveStr(rightKH, rightKH + 1, 0);	//删除右括号 
		flagFolMoveStr();
	}
	//没有括号了,正常运算
	iS = 0, rightKH = strlen(s);
	//1 优先计算乘除
	doCC();
	iS = 0, rightKH = strlen(s);
	//2 再计算加减
	doJJ();
	printf("= %s\n", s);
	
	system("pause");
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43302818/article/details/85254258