刚学过数据结构之后,做了个计算器练习一下,代码比较多,虽然有很多的bug和不足,但是还是起到了学习的作用。
该程序依照Windows自带的计算器进行设计,实现了其中的一些最基本功能:
允许对键盘和计算器面板混合操作,并对输入数据的格式做了限定,保证运算的正确性
运用了栈这种数据结构,可以实现四则混合运算,但必须在按下等号才会显示最终的运算结果,中间运算结果不再显示
存在的bug:在连续按下运算符时将会产生错误的运算结果,
即尚未实现连续输入多个运算符时只按第一个运算符进行运算
首先构造一个顺序栈,实现先进后出的数据结构。可以存储计算过程中出现的表达式,并对表达式进行处理.


1 class SeqStack
2 {
3 private string[] value; // 存储数据的栈
4 private int top; //栈顶元素下标
5 public SeqStack() : this(10) { } //默认元素的空栈
6 public SeqStack(int length) //构造指定元素长度的空栈
7 {
8 this.value = new string[length];
9 this.top = -1; //空栈
10 }
11
12 public Boolean isEmpty() //栈为空返回true
13 {
14 return this.top == -1;
15 }
16 public Boolean push(string element) //入栈
17 {
18 if(element==null) //元素为空
19 {
20 return false;
21 }
22 if(this.top==this.value.Length-1) //栈满,容量扩大一倍
23 {
24 string[] temp = value;
25 value = new string[value.Length * 2];
26 for (int i = 0; i < temp.Length;i++ )
27 {
28 value[i] = temp[i];
29 }
30 }
31 this.top++;
32 this.value[this.top] = element;
33 return true;
34 }
35
36 public string pop() //出栈,返回当前栈顶元素,如栈空返回null
37 {
38 if (!isEmpty())
39 {
40 return this.value[this.top--];
41 }
42 else
43 return null;
44 }
45
46 public string get() //取栈顶元素,未出栈
47 {
48 if (!isEmpty())
49 {
50 return this.value[this.top];
51 }
52 else
53 return null;
54 }
55 }
这样就得到了了一个可以存储我们输入数据或符号的栈结构。
下面就要根据栈中存储的表达式,进行变换和运算了。


1 class Expression
2 {
3 /* toPostfix方法将运算表达式转化为后缀表达式
4 * 如 1+2*3-4转化为后缀表达式为:1 2 3 * + 4 -
5 * 转换规则为:如果遇到数字则原样输出,如果遇到运算符则运算符入栈
6 * 如果要入栈的运算符的优先级比当前栈顶运算符的优先级高则直接入栈,否则
7 * 栈顶元素出栈(输出在数字之后),直到栈顶运算符的优先级比它低,然后将它入栈
8 * */
9 public string toPostfix(string expstr)
10 {
11 SeqStack stack = new SeqStack(expstr.Length);
12 string postfix = "";
13 int i = 0;
14
15 while (i < expstr.Length) //遍历表达式中的各个字符
16 {
17 string str = expstr.Substring(i, 1); //索引为i的字符
18 switch (str)
19 {
20 case "+":
21 case "-": //如果运算符+和-要入栈,那么栈中运算符的优先级必然大于
22 while (!stack.isEmpty()) //或等于+,-,所以直接将栈中运算符出栈
23 {
24 postfix += stack.pop();
25 }
26 stack.push(str);
27 i++;
28 break;
29 case "*":
30 case "/":
31 while (!stack.isEmpty() && stack.get() == "*" || stack.get() == "/") //判断栈顶运算符
32 {
33 postfix += stack.pop(); //是否出栈
34 }
35 stack.push(str);
36 i++;
37 break;
38
39 default:
40 while (str[0] >= '0' && str[0] <= '9' || str[0] == '.') // 遇到数字,原样输出
41 {
42 postfix += str;
43 i++;
44 if (i < expstr.Length)
45 {
46 str = expstr.Substring(i, 1);
47 }
48 else
49 str = "=";
50 }
51 postfix += " ";
52 break;
53 }
54
55 }
56
57 //完全遍历之后,如果栈中还有运算符,则将其全部出栈
58 while (!stack.isEmpty())
59 {
60 postfix += stack.pop();
61 }
62 return postfix; //返回后缀表达式
63 }
64
65 /* 根据后缀表达式来计算结果。
66 * 遍历后缀表达式,每次处理一个字符,遇到数字,转化为数字字符串,入栈
67 * 如果遇到运算符,出栈两个数值进行计算,运算结果入栈
68 * 重复遍历到表达式结束,此时栈中的最后一个元素即为所求结果。
69 * ****/
70 public string value(string postfix)
71 {
72 SeqStack ss = new SeqStack();
73 int i = 0;
74 double result = 0.0;
75 string strResult = "";
76 while (i < postfix.Length)
77 {
78 string str = postfix.Substring(i, 1);
79 if (str[0] >= '0' && str[0] <= '9' || str[0] == '.') //遇到数字要将其入栈,包括处理小数
80 {
81 result = 0;
82 while (str[0] != ' ')
83 {
84 if (str[0] == '.') //遇到小数点
85 {
86 strResult = strResult + ".";
87 i++;
88 str = postfix.Substring(i, 1);
89 }
90 else
91 {
92 if (strResult.Contains(".")) //此时已经将小数点拼接到数字中,再遇到的数字直接
93 {
94 result = Convert.ToInt32(str); //拼接到当前字符串之后即可
95 strResult = strResult + result.ToString();
96 i++;
97 str = postfix.Substring(i, 1);
98 }
99 else
100 {
101 result = result * 10 + Convert.ToInt32(str); //获得大于一位的数字的数值
102 strResult = result.ToString();
103 i++;
104 str = postfix.Substring(i, 1);
105 }
106 }
107 }
108 i++;
109 ss.push(strResult); //将得到的数字入栈
110 strResult = "";
111 }
112
113 //遇到运算符,出栈两个元素开始计算
114 else
115 {
116 double y = Convert.ToDouble(ss.pop());
117 double x = Convert.ToDouble(ss.pop());
118 switch (str)
119 {
120 case "+":
121 result = x + y;
122 break;
123 case "-":
124 result = x - y;
125 break;
126 case "*":
127 result = x * y;
128 break;
129 case "/":
130 if (y != 0)
131 {
132 result = x / y;
133 }
134 else
135 {
136 MessageBox.Show("除数不能为零");
137 while (!ss.isEmpty())
138 {
139 ss.pop();
140 }
141 }
142 break;
143 }
144 ss.push(result.ToString()); //将运算结果入栈
145 i++;
146 }
147
148 }
149 return ss.pop(); //返回最后的运算结果
150 }
151 }
此时程序就已经计算出了表达式的值。在窗体的.cs文件中,要实现窗体面板和键盘的混合输入,并对一些不规范的输入做出处理,这样才能使程序不出现异常,因为考虑了很多种可能出现的输入异常,所以程序用了很多的代码来控制输入,以下分为三段:


1 public partial class Form1 : Form
2 {
3 private string strExp = ""; //表达式字符串
4 private bool flag = false; //当文本框中已经有数字,再按下数字按钮时是否和已有的数字拼接还是清空文本框重新输入
5 Expression exp = new Expression();
6 private string postfix = ""; //表达式的后缀表达式
7 public Form1()
8 {
9 InitializeComponent();
10 }
11
12 //使用窗体上的数字按钮来输入数字,字体的最大长度为十九位
13 public void inputNum(int num)
14 {
15 switch(num)
16 {
17 case 0:
18 if (flag == true)
19 {
20 txtNum.Text = "0";
21 flag = !flag;
22 }
23 else
24 {
25 if (txtNum.Text.Length < 19)
26 {
27 if (txtNum.Text == "0")
28 {
29 txtNum.Text = "";
30 txtNum.Text += "0";
31 }
32 else
33 {
34 txtNum.Text += "0";
35 }
36 }
37 }
38 break;
39 case 1:
40 if (flag == true)
41 {
42 txtNum.Text = "1";
43 flag = !flag;
44 }
45 else
46 {
47 if (txtNum.Text.Length < 19)
48 {
49 if (txtNum.Text == "0")
50 {
51 txtNum.Text = "";
52 txtNum.Text += "1";
53 }
54 else
55 {
56 txtNum.Text += "1";
57 }
58 }
59 }
60 break;
61 case 2:
62 if (flag == true)
63 {
64 txtNum.Text = "2";
65 flag = !flag;
66 }
67 else
68 {
69 if (txtNum.Text.Length < 19)
70 {
71 if (txtNum.Text == "0")
72 {
73 txtNum.Text = "";
74 txtNum.Text += "2";
75 }
76 else
77 {
78 txtNum.Text += "2";
79 }
80 }
81 }
82 break;
83 case 3:
84 if (flag == true)
85 {
86 txtNum.Text = "3";
87 flag = !flag;
88 }
89 else
90 {
91 if (txtNum.Text.Length < 19)
92 {
93 if (txtNum.Text == "0")
94 {
95 txtNum.Text = "";
96 txtNum.Text += "3";
97 }
98 else
99 {
100 txtNum.Text += "3";
101 }
102 }
103 }
104 break;
105 case 4:
106 if (flag == true)
107 {
108 txtNum.Text = "4";
109 flag = !flag;
110 }
111 else
112 {
113 if (txtNum.Text.Length < 19)
114 {
115 if (txtNum.Text == "0")
116 {
117 txtNum.Text = "";
118 txtNum.Text += "4";
119 }
120 else
121 {
122 txtNum.Text += "4";
123 }
124 }
125 }
126 break;
127 case 5:
128 if (flag == true)
129 {
130 txtNum.Text = "5";
131 flag = !flag;
132 }
133 else
134 {
135 if (txtNum.Text.Length < 19)
136 {
137 if (txtNum.Text == "0")
138 {
139 txtNum.Text = "";
140 txtNum.Text += "5";
141 }
142 else
143 {
144 txtNum.Text += "5";
145 }
146 }
147 }
148 break;
149 case 6:
150 if (flag == true)
151 {
152 txtNum.Text = "6";
153 flag = !flag;
154 }
155 else
156 {
157 if (txtNum.Text.Length < 19)
158 {
159 if (txtNum.Text == "0")
160 {
161 txtNum.Text = "";
162 txtNum.Text += "6";
163 }
164 else
165 {
166 txtNum.Text += "6";
167 }
168 }
169 }
170 break;
171 case 7:
172 if (flag == true)
173 {
174 txtNum.Text = "7";
175 flag = !flag;
176 }
177 else
178 {
179 if (txtNum.Text.Length < 19)
180 {
181 if (txtNum.Text == "0")
182 {
183 txtNum.Text = "";
184 txtNum.Text += "7";
185 }
186 else
187 {
188 txtNum.Text += "7";
189 }
190 }
191 }
192 break;
193 case 8:
194 if (flag == true)
195 {
196 txtNum.Text = "8";
197 flag = !flag;
198 }
199 else
200 {
201 if (txtNum.Text.Length < 19)
202 {
203 if (txtNum.Text == "0")
204 {
205 txtNum.Text = "";
206 txtNum.Text += "8";
207 }
208 else
209 {
210 txtNum.Text += "8";
211 }
212 }
213 }
214 break;
215 case 9:
216 if (flag == true)
217 {
218 txtNum.Text = "9";
219 flag = !flag;
220 }
221 else
222 {
223 if (txtNum.Text.Length < 19)
224 {
225 if (txtNum.Text == "0")
226 {
227 txtNum.Text = "";
228
229 txtNum.Text += "9";
230 }
231 else
232 {
233 txtNum.Text += "9";
234 }
235 }
236 }
237 break;
238 case 10:
239 if (txtNum.Text.Length < 19)
240 {
241 txtNum.Text += ".";
242 }
243
244 break;
245
246 default:
247 break;
248 }
249 }


1 //按下窗体上的数字按钮时的事件,即向文本框中输入数字和小数点
2
3 private void btnNum0_Click(object sender, EventArgs e)
4 {
5 inputNum(0);
6 label1.Focus();
7 }
8
9 private void btnNum1_Click(object sender, EventArgs e)
10 {
11 inputNum(1);
12 label1.Focus();
13 }
14
15 private void btnNum2_Click(object sender, EventArgs e)
16 {
17 inputNum(2);
18 label1.Focus();
19 }
20
21 private void btnNum3_Click(object sender, EventArgs e)
22 {
23 inputNum(3);
24 label1.Focus();
25 }
26
27 private void btnNum4_Click(object sender, EventArgs e)
28 {
29 inputNum(4);
30 label1.Focus();
31 }
32
33 private void btnNum5_Click(object sender, EventArgs e)
34 {
35 inputNum(5);
36 label1.Focus();
37 }
38
39 private void btnNum6_Click(object sender, EventArgs e)
40 {
41 inputNum(6);
42 label1.Focus();
43 }
44
45 private void btnNum7_Click(object sender, EventArgs e)
46 {
47 inputNum(7);
48 label1.Focus();
49 }
50
51 private void btnNum8_Click(object sender, EventArgs e)
52 {
53 inputNum(8);
54 label1.Focus();
55 }
56
57 private void btnNum9_Click(object sender, EventArgs e)
58 {
59 inputNum(9);
60 label1.Focus();
61 }
62
63 private void btnDot_Click(object sender, EventArgs e)
64 {
65 InputDot();
66 label1.Focus();
67 }
68
69 //使用Form1的keypress事件实现对文本框的输入,要将其keypreview属性设置为true
70 //原因为:当点击窗体上的数字按钮后,文本框会失去焦点而不能实现输入
71 //只能接受数字,小数点,退格,"+", "-", "*", "/"和回车键
72 private void Form1_KeyPress(object sender, KeyPressEventArgs e)
73 {
74 if (e.KeyChar == '+' || e.KeyChar == '-' || e.KeyChar == '*' || e.KeyChar == '/'
75 ||(Keys)e.KeyChar==Keys.Enter)
76 {
77 flag = true;
78 }
79 if ((e.KeyChar >= '0' && e.KeyChar <= '9')) //输入数字
80 {
81 if (flag == true)
82 {
83 txtNum.Text = e.KeyChar.ToString();
84 flag = !flag;
85 }
86 else
87 {
88 if (txtNum.Text.Length < 19)
89 {
90 if (txtNum.Text == "0")
91 {
92 txtNum.Text = "";
93 txtNum.Text += e.KeyChar.ToString();
94 }
95 else
96 {
97 txtNum.Text += e.KeyChar.ToString();
98 }
99 }
100 }
101 }
102 if(e.KeyChar=='.') //小数点
103 {
104 InputDot();
105 }
106 if(e.KeyChar=='\b') //退格键
107 {
108 if (txtNum.Text.Length != 0)
109 {
110 txtNum.Text = txtNum.Text.Substring(0, txtNum.Text.Length - 1);
111 }
112 if (txtNum.Text == "")
113 {
114 strExp = "";
115 txtNum.Text = "0";
116 flag = true;
117 }
118 }
119 if(e.KeyChar=='+')
120 {
121 operate(1);
122 }
123 if(e.KeyChar=='-')
124 {
125 operate(2);
126 }
127 if(e.KeyChar=='*')
128 {
129 operate(3);
130 }
131 if(e.KeyChar=='/')
132 {
133 operate(4);
134 }
135
136 if((Keys)e.KeyChar==Keys.Enter) //回车键,计算结果
137 {
138
139 flag = true;
140 numManage();
141 strExp += txtNum.Text;
142 postfix = exp.toPostfix(strExp);
143 txtNum.Text = exp.value(postfix);
144
145 strExp = "";
146
147 }
148 }
149
150 //当鼠标在文本框上按下的时候不显示光标,此时将控件焦点转移到一个不可见的lable上
151 private void txtNum_MouseDown(object sender, MouseEventArgs e)
152 {
153 label1.Focus();
154 }
155
156
157 // 窗体上backspace按钮事件
158 private void btnBsp_Click(object sender, EventArgs e)
159 {
160 if (txtNum.Text.Length != 0)
161 {
162 txtNum.Text = txtNum.Text.Substring(0, txtNum.Text.Length - 1);
163
164 }
165 if (txtNum.Text == "" || txtNum.Text == "-")
166 {
167 txtNum.Text = "0";
168 flag = true;
169 }
170 label1.Focus();
171 }
172
173 private void btnClear_Click(object sender, EventArgs e) //清空结果
174 {
175 txtNum.Text = "0";
176 strExp = "";
177 label1.Focus();
178
179 }
180
181 //对小数点的输入
182 public void InputDot()
183 {
184 if (flag == true)
185 {
186 txtNum.Text = "0.";
187 flag = !flag;
188 }
189 else
190 {
191 if (txtNum.Text.Length == 0) //文本框为空时点击小数点
192 {
193 txtNum.Text += "0.";
194 }
195 if (!txtNum.Text.Contains(".")) //数字中是否已经存在小数点
196 {
197 inputNum(10);
198 }
199 }
200 }
201
202 //对输入的数据进行处理
203 public void numManage()
204 {
205 //如果数字中没有小数点,则不需要处理
206 if (txtNum.Text.Contains("."))
207 {
208 if (txtNum.Text.Substring(txtNum.Text.Length - 1, 1) == ".") //小数点在数字最后一位,舍掉
209 {
210 txtNum.Text = txtNum.Text.Substring(0, txtNum.Text.Length - 1);
211 }
212 else
213 {
214 while (txtNum.Text.Substring(txtNum.Text.Length - 1, 1) == "0")
215 {
216 txtNum.Text = txtNum.Text.Substring(0, txtNum.Text.Length - 1);
217 if (txtNum.Text.Substring(txtNum.Text.Length - 1, 1) == ".")
218 {
219 txtNum.Text = txtNum.Text.Substring(0, txtNum.Text.Length - 1);
220 break;
221 }
222 }
223 }
224 }
225 }
226
227 private void btnAdd_Click(object sender, EventArgs e)
228 {
229 operate(1);
230 label1.Focus();
231 }
232
233 private void btnSub_Click(object sender, EventArgs e)
234 {
235 operate(2);
236 label1.Focus();
237 }
238
239 private void btuMul_Click(object sender, EventArgs e)
240 {
241 operate(3);
242 label1.Focus();
243 }
244
245 private void btnDiv_Click(object sender, EventArgs e)
246 {
247 operate(4);
248 label1.Focus();
249 }
250
251 private void btnEqu_Click(object sender, EventArgs e)
252 {
253 flag = true;
254 numManage();
255 strExp += txtNum.Text;
256 postfix = exp.toPostfix(strExp);
257 txtNum.Text = exp.value(postfix);
258 strExp = "";
259 label1.Focus();
260 }
261
262 public void operate(int ope)
263 {
264
265 flag = true;
266 numManage();
267 strExp += txtNum.Text;
268 opera(ope);
269 }
270
271 public void opera(int ope)
272 {
273 switch(ope)
274 {
275 case 1:
276 strExp += "+";
277 break;
278
279 case 2:
280 strExp += "-";
281 break;
282
283 case 3:
284 strExp += "*";
285
286 break;
287
288 case 4:
289
290 strExp += "/";
291
292 break;
293
294 default:
295 break;
296 }
297 }


1 private void btnSqr_Click(object sender, EventArgs e)
2 {
3 if (txtNum.Text != "")
4 {
5 numManage();
6 if (Convert.ToDouble(txtNum.Text) >= 0)
7 {
8 flag = true;
9 txtNum.Text = Math.Sqrt(Convert.ToDouble(txtNum.Text)).ToString();
10 }
11 else
12 {
13 MessageBox.Show("被开方数不能小于零");
14 txtNum.Text = "0";
15 }
16 }
17 label1.Focus();
18 }
19
20 private void button1_Click(object sender, EventArgs e)
21 {
22 if (txtNum.Text != "")
23 {
24 numManage();
25 if (Convert.ToDouble(txtNum.Text) != 0)
26 {
27 flag = true;
28 txtNum.Text = (1 / Convert.ToDouble(txtNum.Text)).ToString();
29 }
30 if (Convert.ToDouble(txtNum.Text) == 0)
31 {
32 MessageBox.Show("除数不能为零");
33 }
34 }
35 label1.Focus();
36 }
37
38 private void btnSin_Click(object sender, EventArgs e)
39 {
40 if (txtNum.Text != "")
41 {
42 numManage();
43 flag = true;
44 txtNum.Text = Math.Sin(Convert.ToDouble(txtNum.Text) * Math.PI / 180).ToString();
45 }
46 label1.Focus();
47 }
48
49 private void btnPi_Click(object sender, EventArgs e)
50 {
51 flag = true;
52 txtNum.Text = "3.14159265358979";
53 label1.Focus();
54 }
55
56 private void btnLog_Click(object sender, EventArgs e)
57 {
58 if (txtNum.Text != "")
59 {
60 numManage();
61 if (Convert.ToDouble(txtNum.Text) > 0)
62 {
63 flag = true;
64 txtNum.Text = Math.Log10(Convert.ToDouble(txtNum.Text)).ToString();
65 }
66 else
67 {
68 MessageBox.Show("输入的数字应大于零");
69 txtNum.Text = "0";
70 }
71 }
72 label1.Focus();
73 }
74
75 private void btnCos_Click(object sender, EventArgs e)
76 {
77 if (txtNum.Text != "")
78 {
79 numManage();
80 flag = true;
81 txtNum.Text = Math.Cos(Convert.ToDouble(txtNum.Text) * Math.PI / 180).ToString();
82 }
83 label1.Focus();
84 }
85
86 private void btnTan_Click(object sender, EventArgs e)
87 {
88 numManage();
89 if (txtNum.Text != "")
90 {
91 if (((Convert.ToDouble(txtNum.Text) - 90) / 180).ToString().Contains("."))
92 {
93 flag = true;
94 txtNum.Text = Math.Tan(Convert.ToDouble(txtNum.Text) * Math.PI / 180).ToString();
95 }
96 else
97 {
98 MessageBox.Show("输入不正确");
99 txtNum.Text = "0";
100 }
101 }
102 label1.Focus();
103 }
104
105 private void btnFab_Click(object sender, EventArgs e)
106 {
107 if (txtNum.Text != "")
108 {
109 numManage();
110 if (!txtNum.Text.Contains(".") && (Convert.ToDouble(txtNum.Text) >= 0))
111 {
112 if (txtNum.Text == "正无穷大")
113 {
114 return;
115 }
116 else
117 {
118 flag = true;
119 txtNum.Text = fab(Convert.ToInt32(txtNum.Text)).ToString();
120 }
121 }
122 else
123 {
124 MessageBox.Show("输入数字有误");
125 txtNum.Text = "0";
126 }
127 }
128 label1.Focus();
129 }
130 public long fab(int n)
131 {
132 if (n==0||n == 1)
133 {
134 return 1;
135 }
136 else
137 {
138 return n * fab(n - 1);
139 }
140 }
141
142 private void Form1_Load(object sender, EventArgs e)
143 {
144 txtNum.Text = "0";
145 flag = true;
146 }
147 }