表示式求值之字串處理
阿新 • • 發佈:2018-12-27
分析:由於要求用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; }