如何在C#中執行數學表示式字串
阿新 • • 發佈:2019-02-08
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>