1. 程式人生 > >如何在C#中執行數學表示式字串

如何在C#中執行數學表示式字串

1 /**/ 2 /// <summary> 3 /// 動態求值 4 /// </summary> 5 public class Evaluator 6 { 7 /**/ 8 /// <summary> 9 /// 計算結果,如果表示式出錯則丟擲異常 10 /// </summary> 11 /// <param name="statement">表示式,如"1+2+3+4"</param> 12 /// <returns>
結果</returns> 13 public static object Eval(string statement) 14 { 15 if (statement.Trim() != string.Empty) 16 { 17 Evaluator evaluator = new Evaluator(); 18 return evaluator.GetFormulaResult(statement); 19 }
20 else 21 { 22 return null; 23 } 24 } 25 26 27 private object GetFormulaResult(string s) 28 { 29 if (s == "") 30 { 31 return null; 32 } 33 string
S = BuildingRPN(s); 34 35 string tmp = ""; 36 System.Collections.Stack sk = new System.Collections.Stack(); 37 38 char c = ' '; 39 System.Text.StringBuilder Operand = new System.Text.StringBuilder(); 40 double x, y; 41 for (int i = 0; 42 i < S.Length; 43 i++) 44 { 45 c = S[i]; 46 //added c==',' for germany culture 47 if (char.IsDigit(c) || c == '.' || c == ',') 48 { 49 //資料值收集. 50 Operand.Append(c); 51 } 52 else if (c == ' ' && Operand.Length > 0) 53 { 54 #region 運算數轉換 55 try 56 { 57 tmp = Operand.ToString(); 58 if (tmp.StartsWith("-"))//負數的轉換一定要小心...它不被直接支援. 59 { 60 //現在我的演算法裡這個分支可能永遠不會被執行. 61 sk.Push(-((double)Convert.ToDouble(tmp.Substring(1, tmp.Length - 1)))); 62 } 63 else 64 { 65 sk.Push(Convert.ToDouble(tmp)); 66 } 67 } 68 catch 69 { 70 return null; // 71 } 72 Operand = new System.Text.StringBuilder(); 73 #endregion 74 } 75 else if (c == '+'//運算子處理.雙目運算處理. 76 || c == '-' 77 || c == '*' 78 || c == '/' 79 || c == '%' 80 || c == '^') 81 { 82 #region 雙目運算 83 if (sk.Count > 0)/*如果輸入的表示式根本沒有包含運算子.或是根本就是空串.這裡的邏輯就有意義了.*/ 84 { 85 y = (double)sk.Pop(); 86 } 87 else 88 { 89 sk.Push(0); 90 break; 91 } 92 if (sk.Count > 0) 93 x = (double)sk.Pop(); 94 else 95 { 96 sk.Push(y); 97 break; 98 } 99 switch (c) 100 { 101 case '+': 102 sk.Push(x + y); 103 break; 104 case '-': 105 sk.Push(x - y); 106 break; 107 case '*': 108 if (y == 0) 109 { 110 sk.Push(x * 1); 111 } 112 else 113 { 114 sk.Push(x * y); 115 } 116 break; 117 case '/': 118 if (y == 0) 119 { 120 sk.Push(x / 1); 121 } 122 else 123 { 124 sk.Push(x / y); 125 } 126 break; 127 case '%': 128 sk.Push(x % y); 129 break; 130 case '^':// 131 if (x > 0)// 132 { 133 //我原本還想,如果被計算的數是負數,又要開真分數次方時如何處理的問題.後來我想還是算了吧. 134 sk.Push(System.Math.Pow(x, y)); 135 // 136 } 137 // 138 else// 139 { 140 // 141 double t = y; 142 // 143 string ts = ""; 144 // 145 t = 1 / (2 * t); 146 // 147 ts = t.ToString(); 148 // 149 if (ts.ToUpper().LastIndexOf('E') > 0)// 150 { 151 // 152 ; 153 // 154 } 155 // 156 } 157 break; 158 } 159 #endregion 160 } 161 else if (c == '!')//單目取反. ) 162 { 163 sk.Push(-((double)sk.Pop())); 164 } 165 } 166 if (sk.Count > 1) 167 { 168 return null;//; 169 } 170 if (sk.Count == 0) 171 { 172 return null;//; 173 } 174 return sk.Pop(); 175 } 176 /**/ 177 /// <summary> 178 /// 179 /// </summary> 180 private string BuildingRPN(string s) 181 { 182 System.Text.StringBuilder sb = new System.Text.StringBuilder(s); 183 System.Collections.Stack sk = new System.Collections.Stack(); 184 System.Text.StringBuilder re = new System.Text.StringBuilder(); 185 186 char c = ' '; 187 //sb.Replace( " ","" ); 188 //一開始,我只去掉了空格.後來我不想不支援函式和常量能濾掉的全OUT掉. 189 for (int i = 0; 190 i < sb.Length; 191 i++) 192 { 193 c = sb[i]; 194 //added c==',' for german culture 195 if (char.IsDigit(c) || c == ',')//數字當然要了. 196 re.Append(c); 197 //if( char.IsWhiteSpace( c )|| 198 char.IsLetter(c);//如果是空白,那麼不要.現在字母也不要. 199 //continue; 200 switch (c)//如果是其它字元...列出的要,沒有列出的不要. 201 { 202 case '+': 203 case '-': 204 case '*': 205 case '/': 206 case '%': 207 case '^': 208 case '!': 209 case '(': 210 case ')': 211 case '.': 212 re.Append(c); 213 break; 214 default: 215 continue; 216 } 217 } 218 sb = new System.Text.StringBuilder(re.ToString()); 219 #region 對負號進行預轉義處理.負號變單目運算子求反. 220 for (int i = 0; i < sb.Length - 1; i++) 221 if (sb[i] == '-' && (i == 0 || sb[i - 1] == '(')) 222 sb[i] = '!'; 223 //字元轉義. 224 #endregion 225 #region 將中綴表示式變為字尾表示式. 226 re = new System.Text.StringBuilder(); 227 for (int i = 0; 228 i < sb.Length; 229 i++) 230 { 231 if (char.IsDigit(sb[i]) || sb[i] == '.')//如果是數值. 232 { 233 re.Append(sb[i]); 234 //加入字尾式 235 } 236 else if (sb[i] == '+' 237 || sb[i] == '-' 238 || sb[i] == '*' 239 || sb[i] == '/' 240 || sb[i] == '%' 241 || sb[i] == '^' 242 || sb[i] == '!')//. 243 { 244 #region 運算子處理 245 while (sk.Count > 0) //棧不為空時 246 { 247 c = (char)sk.Pop(); 248 //將棧中的操作符彈出. 249 if (c == '(') //如果發現左括號.停. 250 { 251 sk.Push(c); 252 //將彈出的左括號壓回.因為還有右括號要和它匹配. 253 break; 254 //中斷. 255 } 256 else 257 { 258 if (Power(c) < Power(sb[i]))//如果優先順序比上次的高,則壓棧. 259 { 260 sk.Push(c); 261 break; 262 } 263 else 264 { 265 re.Append(' '); 266 re.Append(c); 267 } 268 //如果不是左括號,那麼將操作符加入字尾式中. 269 } 270 } 271 sk.Push(sb[i]); 272 //把新操作符入棧. 273 re.Append(' '); 274 #endregion 275 } 276 else if (sb[i] == '(')//基本優先順序提升 277 { 278 sk.Push('('); 279 re.Append(' '); 280 } 281 else if (sb[i] == ')')//基本優先順序下調 282 { 283 while (sk.Count > 0) //棧不為空時 284 { 285 c = (char)sk.Pop(); 286 //pop Operator 287 if (c != '(') 288 { 289 re.Append(' '); 290 re.Append(c); 291 //加入空格主要是為了防止不相干的資料相臨產生解析錯誤. 292 re.Append(' '); 293 } 294 else 295 break; 296 } 297 } 298 else 299 re.Append(sb[i]); 300 } 301 while (sk.Count > 0)//這是最後一個彈棧啦. 302 { 303 re.Append(' '); 304 re.Append(sk.Pop()); 305 } 306 #endregion 307 re.Append(' '); 308 return FormatSpace(re.ToString()); 309 //在這裡進行一次表示式格式化.這裡就是字尾式了. 310 } 311 312 /// <summary> 313 /// 優先級別測試函式. 314 /// </summary> 315 /// <param name="opr"></param> 316 /// <returns></returns>