1. 程式人生 > >表示式求值之字串處理

表示式求值之字串處理

分析:由於要求用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;
}