开发环境:win10+Visual Studio2019
先附上源码链接:代码 。·欢迎start。
第一个C#实验就是实现一个计算器。花了一晚上的时间终于写好了。计算要考虑优先级,那么首先就得想到使用到后缀表达式了。这个知识点在学习数据结构的时候应该很熟悉了。博主在处理表达式求值的部分就直接拿了之前pta上面的代码稍微改了一下。现在说一下整体的设计思路吧。
这个计算器实现的功能除了加减乘除外还有删除操作(跟win上面的计算器一样),以及求一个数的倒数,开根号,以及取反的功能。首先先从工具箱拖出所需要的控件摆放好,效果图如下。显示我用了两个label,一个显示当前正在输入的数,一个显示总的输入。
当我们点击一个按钮时,可以获取响应的内容。如果对于每个按钮都绑定一个按钮,那么代码将会非常的冗余,所以博主把这些按钮分成了五类,对应四个事件。
0-9
以及.
。+
、-
、*
、/
、%
。√
、1/x
、±
。<-
、CE
、C
。+
。接下来看下各部分的代码。
初始状态只能输入小数点或者数字。运算符均不可输入。当有数字输入后才可以输入运算符。
当输入单目运算符后,就只能输入双目运算符。
当输入双目运算符后,是能输入数字,输入单目或者双目运算符或者求值都是无效的。
具体看后面代码就清楚了。这样理解看起来可能比较抽象。(建议下载完整代码看)
这部分是属性。
private ArrayList list = new ArrayList(); //存输入的数字和运算符
//防呆处理
private bool op_flag = false; //是否可以输入双目运算符
private bool num_flag = true; //是否可以输入数字
private bool sing_op_flag = false; //是否可以输入单目运算符
private bool dot_flag = true; //是否可以输入 小数点
private bool is_clear = true; //是否清楚
处理数值输入。
private void input_Number(object sender, EventArgs e)
{
if (is_clear) input_Delete(this.btn_clsall, e);
if (num_flag)
{
if (sender == btn_dot)
{
if (!dot_flag) return;
dot_flag = false;
// 防止出现 以.开头的数
if (this.lab_num.Text.Length > 0)
this.lab_num.Text += ".";
else
this.lab_num.Text += "0.";
}
else
{
// 防止出现 01等0开头的数
if (this.lab_num.Text.Length < 2 && this.lab_num.Text[0] == '0')
this.lab_num.Text = "";
this.lab_num.Text += ((Button)sender).Text;
}
// 处理哪些按钮接下去可用
sing_op_flag = true;
op_flag = true;
}
}
处理输入双目运算符。
private void input_Op(object sender, EventArgs e)
{
if (op_flag)
{
// 处理显示时的小细节
//若之前输入的不是是单目运算符,就加上数字和运算符,否则就只显示运算符。
if (sing_op_flag == true)
{
this.lab_show.Text += (this.lab_num.Text + ((Button)sender).Text);
list.Add(Double.Parse(this.lab_num.Text));
}
else
this.lab_show.Text += (((Button)sender).Text);
list.Add(((Button)sender).Text);
// 处理哪些按钮接下去可用
op_flag = false;
sing_op_flag = false;
num_flag = true;
dot_flag = true;
this.lab_num.Text = "0";
}
}
处理单目运算符
private void input_Single_Op(object sender, EventArgs e)
{
if(sing_op_flag)
{
if (sender == btn_sqrt)
{
list.Add(Math.Sqrt( Double.Parse(lab_num.Text)));
lab_show.Text += "Sqrt(" + lab_num.Text + ")";
}
if(sender==btn_rec)
{
list.Add(1/Double.Parse(lab_num.Text));
lab_show.Text += "(1/" + lab_num.Text + ")";
}
if(sender==btn_neg)
{
list.Add(-Math.Abs(Double.Parse(lab_num.Text)));
lab_show.Text += "(-" + lab_num.Text + ")";
}
.
// 处理哪些按钮接下去可用
input_Delete(btn_clsone, e);
num_flag = false;
sing_op_flag = false;
op_flag = true;
}
}
处理删除按钮。
C
:删除所有输入。CE
:删除当前输入的数字。<-
:删除一个数字。
private void input_Delete(object sender, EventArgs e)
{
if (sender == btn_del) {
string str = (string)this.lab_num.Text;
if (str.Length != 0)
this.lab_num.Text = str.Remove(str.Length-1);
if (this.lab_num.Text.Length == 0)
this.lab_num.Text = "0";
return;
}
if (sender == btn_clsone)
this.lab_num.Text = "0";
if (sender == btn_clsall)
{
this.lab_show.Text = "";
this.lab_num.Text = "0";
op_flag = sing_op_flag = false;
num_flag = true;
list.Clear();
is_clear = false;
}
dot_flag = true;
}
中缀转后缀。传入的参数是一个字符串。
private Queue change(string str)
{
string temp = "";
string str1 = "";
Stack s=new Stack();
Queue que=new Queue();
int i;
for (i = 0; i < str.Length; i++)
{
temp="";
if (((i == 0 ) && (str[i] == '+'
)) || (str[i] >= '0' && str[i] <= '9'))
{
if (i >= str.Length)
break;
while (i < str.Length && (((i == 0 )
&& (str[i] == '+')) || (str[i] >= '0' && str[i] <= '9' || str[i] == '.')))
{
if (str[i] != '+')
temp += str[i];
i++;
}
if (temp.Length>0)
que.Enqueue(temp);
}
if (i >= str.Length) break;
if ( str[i] == '*' || str[i] == '/'||str[i]=='%')
{
str1 = str[i].ToString();
s.Push(str1);
}
else if (str[i] == '+' || str[i] == '-')
{
if (s.Count()==0)
{
str1 = str[i].ToString();
s.Push(str1);
}
else
{
do
{
str1 = s.Peek();
s.Pop();
que.Enqueue(str1);
} while (s.Count()!=0);
str1 = str[i].ToString();
s.Push(str1);
}
}
str1="";
}
while (s.Count()>0)
{
str1 = s.Peek();
s.Pop();
que.Enqueue(str1);
}
return que;
}
求值处理。
private void result(object sender, EventArgs e)
{
if (!op_flag) return;
list.Add(Double.Parse(lab_num.Text));
if(!lab_num.Text.Equals("0"))
lab_show.Text += lab_num.Text;
try
{
string str = "";
// 把输入的表达式转成字符串,其实可以直接用string替换掉list,不过懒得改了。
foreach(var val in list)
{
str = str + val.ToString();
}
// 获取后缀表达式,用队列形式存储。
Queue que = change(str);
Double num1, num2;
string ans;
Stack st = new Stack();
foreach (var val in que)
{
// 因为que里面要么是运算符要么是数值
if (val[0] >= '0' && val[0] <= '9')
{
st.Push(Double.Parse(val));
}
else
{
Double num = 0;
num1 = st.Pop();
num2 = st.Pop();
char op = val[0];
if (op == '+') num = num1 + num2;
if (op == '-') num = num2 - num1;
if (op == '*') num = num1 * num2;
if (op == '%')
{
if (num1 == 0)
{
lab_show.Text = "输出不能为零";
return;
}
num = num2 % num1;
}
if (op == '/')
{
if (num1 == 0)
{
lab_show.Text = "输出不能为零";
return;
}
num = num2 / num1;
}
st.Push(num);
}
}
ans = st.Peek().ToString();
lab_show.Text += "=" + ans;
}catch(Exception exce)
{
lab_show.Text = exce.Message;
}finally
{
is_clear = true;
}
}