- 控件的设置
我们先依次将控件摆放好,如图:
我们用groupbox将操作栏统一整合,方便后续我们的位置调整
在groupbox里面,我们将一系列的button控件置于上方,用button来进行选择
文本框添加textbox,按钮添加button,设置好按钮属性,在闪电图标对应的事件中选择添加Click事件。
- 代码难点分析
我们首先来分析一下计算器的难点;
能够识别四则运算的优先级和括号 的计算器,难点在于如何在遇到乘号,除号或者是括号的时候暂时不计算前面的加法和减法,而先去计算优先级高的运算符。
在这里,我们使用栈这一数据结构,利用其先进后出的特性,将读到的第一个符号压入符号栈,在读到下一个符号,与栈顶的符号的优先级进行比较,若优先级比栈顶的那个符号低,就将栈顶符号的连接的两个数字(数字存在数字栈中)进行运算,否则将当前符号压入符号栈。遍历完一遍之后算式就只剩加减号了(或者直接得出答案了),然后就很简单了。
- 代码具体实现
我们使用两个栈结构,一个存数字,一个存运算符。
为了实现用栈计算算数表达式的值,需设置两个工作栈:用于存储运算符的栈fn,以及用于存储操作数及中间结果的栈num。
计算步骤:
定义index对表达式进行扫描; 创建两个栈,一个栈存放数字,称为数栈(num);一个栈存放符号,称为符号栈(fn);
当遇到数字时,入数栈;(注意字符位数,数字有可能是多位数)
2.1 若是字符串最后一个的时候,直接入数栈
2.2 若不是最后一个字符,需要再往后判断一位,若后一位不是符号,继续遍历,并将该字符拼接;若是则进数栈
当遇到符号时,入符号栈
3.1如果符号栈为空,则直接入栈
3.2如果符号栈不为空;则需要判断优先级;
3.2.1判断优先级,若当前符号的优先级小于栈顶符号的优先级,则栈顶符号出栈,同时数栈中两个数字出栈;进行计算,得出的结果入数栈,当前符号入栈。
3.2.2若优先级大于前一个符号,则继续进行入栈操作。
当表达式遍历完后,则两个数字、一个符号依次出栈,进行操作,最后栈中剩的最后一个数为结果
通过使用两个栈来计算表达式的值:一个存储操作数(数字),另一个存储运算符。
扫描表达式:逐字符处理表达式。
- 跳过空格。
- 如果是数字或小数点,提取整个数字并将其压入操作数栈。
- 如果是左括号,将其压入运算符栈。
- 如果是右括号,弹出运算符栈直到遇到左括号,并计算这些运算符对应的操作数。
- 如果是运算符,比较其与运算符栈顶运算符的优先级:
- 若当前运算符优先级低或相等,先处理栈顶运算符,再将当前运算符压入栈。
- 若当前运算符优先级高,直接将其压入栈。
处理剩余运算符:最后阶段,处理所有留在栈中的运算符,直到运算符栈为空。
先来给出每个控件对应的事件的实现,每个数字,我们都将其追加到textbox里面,下面我们以数字3为例
private void button3_Click(object sender, EventArgs e)
{
//3
if (this.textBox1.Text.Contains("="))
{
}
else
{
this.textBox1.Text += this.button3.Text;
}
}
我们将小数点也做同样处理;
private void button_point_Click(object sender, EventArgs e)
{
//.
if (this.textBox1.Text.Contains("="))
{
}
else
{
this.textBox1.Text += this.button_point.Text;
}
}
对退格也做相应的处理;
private void button_tui_Click(object sender, EventArgs e)
{
//退格
string s = this.textBox1.Text;
if(s.Length > 0)
{
this.textBox1.Text = s.Substring(0,s.Length-1);
}
}
然后对运算符的输入,做类似的处理,以下我以乘法为例;
private void button_mul_Click(object sender, EventArgs e)
{
//乘号
string s = this.textBox1.Text;
if (s.Length > 0)
{
s = s.Substring(s.Length - 1);
if (this.textBox1.Text.Contains("=") ||
this.textBox1.Text.Substring(this.textBox1.Text.Length - 1) == "+" ||
this.textBox1.Text.Substring(this.textBox1.Text.Length - 1) == "-" ||
this.textBox1.Text.Substring(this.textBox1.Text.Length - 1) == "*" ||
this.textBox1.Text.Substring(this.textBox1.Text.Length - 1) == "/")
{
}
else
{
this.textBox1.Text += this.button_mul.Text;
calculate_symbol[symbol_index] = "*";
symbol_index++;
}
}
}
然后是清除功能,在这我们调用Focus和Clear函数来实现功能。
private void button_AC_Click(object sender, EventArgs e)
{
//清除
textBox1.Focus();
textBox1.Clear();
}
接下来,就是最重要的计算部分,也就是我们的“=”控件对应的click事件;
private void button_result_Click(object sender, EventArgs e)
{
string s = textBox1.Text;
if (s[0] == '-')
{
s = '0' + s;
}
//如果表达式结尾是运算符号或者小数点,直接忽略
if (s[s.Length - 1] == '+' || s[s.Length - 1] == '-' || s[s.Length - 1] == '*' || s[s.Length - 1] == '/' || s[s.Length - 1] == '.')
{
s = s.Remove(s.Length - 1, 1);
}
Stack<double> numstack = new Stack<double>();//数字栈
Stack<char> symstack = new Stack<char>();//符号栈
//遍历字符串,截取操作数和运算符,分别入栈来计算
for (int i = 0; i < s.Length; i++)
{
//不是符号就继续
if (s[i] != '+' && s[i] != '-' && s[i] != '*' && s[i] != '/')
{
//继续遍历来判断操作数有多长
for (int j = i; j < s.Length; j++)
{
//遇到符号,遍历停止
if (s[j] == '+' || s[j] == '-' || s[j] == '*' || s[j] == '/')
{
//将操作数存进数字栈
numstack.Push(float.Parse(s.Substring(i, j - i)));
//遍历结束
i = j - 1;
break;
}
//遍历到最后一位
if(j == s.Length - 1)
{
//最后一个操作数直接压栈
numstack.Push(double.Parse(s.Substring(i, j + 1 - i)));
//遍历结束
i = s.Length;
}
}
}
//遍历到运算符
else
{
//符号栈为空,直接入栈
//第一个运算符直接入栈
if (symstack.Count == 0)
{
symstack.Push(s[i]);
}
//后续进行优先级判断
//准备入栈元素优先级低于或者等于栈顶元素,先从栈里面拿出两个运算数和一个符号计算后方可入栈
else if (((s[i] == '+' || s[i] == '-') && (symstack.Peek() == '*' || symstack.Peek() == '/'))
|| ((s[i] == '+' || s[i] == '-') && (symstack.Peek() == '+' || symstack.Peek() == '-'))
|| ((s[i] == '*' || s[i] == '/') && (symstack.Peek() == '*' || symstack.Peek() == '/')))
{
//拿出数字栈最上面两个操作数
double a1 = numstack.Pop();
double b1 = numstack.Pop();
//定义当前运算符为当前栈顶运算符
char op1 = symstack.Peek();
switch (op1)
{
case '+':
{
numstack.Push(b1 + a1);
break;
}
case '-':
{
numstack.Push(b1 - a1);
break;
}
case '*':
{
//结果入数字栈
numstack.Push(b1 * a1);
break;
}
case '/':
{
numstack.Push(b1 / a1);
break;
}
}
//运算完毕,丢弃之前的栈顶运算符,存入新的
symstack.Pop();
symstack.Push(s[i]);
}
//其他情况不涉及优先级,直接入栈
else
{
symstack.Push(s[i]);
}
}
}
//循环结束
//操作数和运算符分别入栈,准备计算
while (symstack.Count != 0)
{
char op2 = symstack.Pop();
double a = numstack.Pop();
double b = numstack.Pop();
switch (op2)
{
case '+':
{
numstack.Push(a + b);
break;
}
case '-':
{
numstack.Push(b - a);
break;
}
case '*':
{
//结果入数字栈
numstack.Push(a * b);
break;
}
case '/':
{
numstack.Push(b / a);
break;
}
}
}
//输出结果
s = numstack.Peek().ToString("f1");
textBox1.Text += "=";
textBox1.Text += s;
}
我们来对代码进行详细分析:
- 初始设置:
- 从 textBox1 中获取输入字符串 s。
- 如果字符串以 '-'(负数)开头,会在开头添加 '0',以确保解析时一致性。
- 移除字符串末尾的操作符或小数点,确保字符串以有效的数字表达式结尾。
- 堆栈初始化:
- numstack:存放数字操作数的堆栈。
- symstack:存放操作符(+、-、*、/)的堆栈。
- 处理输入字符串:
- 遍历字符串 s 中的每个字符。
- 如果字符是数字,则收集整个数字(包括小数部分),并将其压入 numstack。
- 如果字符是操作符(+、-、*、/),则利用 symstack 管理操作符的优先级:
- 如果当前操作符比 symstack 顶部的操作符优先级更高,则使用 numstack 和 symstack 的顶部操作数执行计算。
- 否则,将当前操作符推入 symstack。
- 最终计算:
- 处理完 s 中的所有字符后,执行 symstack 中剩余的所有操作,直到 symstack 为空。这确保按正确的优先级顺序执行所有计算。
- 显示结果:
- 最终结果格式化为一位小数("f1")并显示在 textBox1 中,附加上 "="。
这就是在winfrom平台上一个对于计算器的一个基本实现,本段代码还有一些操作未考虑;
- 错误处理:代码假设输入有效,没有处理可能的异常(如除以零或无效输入)。
- 操作符优先级:代码处理基本的操作符优先级(* 和 / 优先级高于 + 和 -),但未考虑括号以改变优先级。
- 输入验证:假定输入是有效的算术表达式,没有空格或其他格式问题。
- 界面交互:直接修改 textBox1.Text 既用于输入又用于输出,对于更复杂的界面需求可能不够理想。
附录(完整代码)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsCa
{
public partial class Form1 : Form
{
string[] calculate_symbol = new string[10000];
int symbol_index = 0;
public Form1()
{
InitializeComponent();
}
private void button9_Click(object sender, EventArgs e)
{
//减号
string s = this.textBox1.Text;
if (s.Length > 0)
{
s = s.Substring(s.Length - 1);
if (this.textBox1.Text.Contains("=") ||
this.textBox1.Text.Substring(this.textBox1.Text.Length - 1) == "+" ||
this.textBox1.Text.Substring(this.textBox1.Text.Length - 1) == "-" ||
this.textBox1.Text.Substring(this.textBox1.Text.Length - 1) == "*" ||
this.textBox1.Text.Substring(this.textBox1.Text.Length - 1) == "/")
{
}
else
{
this.textBox1.Text += this.button_sub.Text;
calculate_symbol[symbol_index] = "-";
symbol_index++;
}
}
}
private void button18_Click(object sender, EventArgs e)
{
//除号
string s = this.textBox1.Text;
if (s.Length > 0)
{
s = s.Substring(s.Length - 1);
if (this.textBox1.Text.Contains("=") ||
this.textBox1.Text.Substring(this.textBox1.Text.Length - 1) == "+" ||
this.textBox1.Text.Substring(this.textBox1.Text.Length - 1) == "-" ||
this.textBox1.Text.Substring(this.textBox1.Text.Length - 1) == "*" ||
this.textBox1.Text.Substring(this.textBox1.Text.Length - 1) == "/")
{
}
else
{
this.textBox1.Text += this.button_del.Text;
calculate_symbol[symbol_index] = "/";
symbol_index++;
}
}
}
private void button1_Click(object sender, EventArgs e)
{
//1
//MessageBox.Show(this.button1.Text);
//this.textBox1.Text += this.button1.Text;//追加数字1
//this.textBox1.AppendText(this.button1.Text);
//int a = int.Parse(this.button1.Text);
if (this.textBox1.Text.Contains("="))
{
}else{
this.textBox1.Text += this.button1.Text;
}
}
private void button9_Click_1(object sender, EventArgs e)
{
//9
if (this.textBox1.Text.Contains("="))
{
}
else
{
this.textBox1.Text += this.button9.Text;
}
}
private void button_tui_Click(object sender, EventArgs e)
{
//退格
string s = this.textBox1.Text;
if(s.Length > 0)
{
this.textBox1.Text = s.Substring(0,s.Length-1);
}
}
private void button_AC_Click(object sender, EventArgs e)
{
//清除
textBox1.Focus();
textBox1.Clear();
}
private void button_add_Click(object sender, EventArgs e)
{
//加号
string s = this.textBox1.Text;
if (s.Length > 0)
{
s = s.Substring(s.Length - 1);
if (this.textBox1.Text.Contains("=") ||
this.textBox1.Text.Substring(this.textBox1.Text.Length - 1) == "+" ||
this.textBox1.Text.Substring(this.textBox1.Text.Length - 1) == "-" ||
this.textBox1.Text.Substring(this.textBox1.Text.Length - 1) == "*" ||
this.textBox1.Text.Substring(this.textBox1.Text.Length - 1) == "/")
{
}
else
{
this.textBox1.Text += this.button_add.Text;
calculate_symbol[symbol_index] = "+";
symbol_index++;
}
}
}
private void button_mul_Click(object sender, EventArgs e)
{
//乘号
string s = this.textBox1.Text;
if (s.Length > 0)
{
s = s.Substring(s.Length - 1);
if (this.textBox1.Text.Contains("=") ||
this.textBox1.Text.Substring(this.textBox1.Text.Length - 1) == "+" ||
this.textBox1.Text.Substring(this.textBox1.Text.Length - 1) == "-" ||
this.textBox1.Text.Substring(this.textBox1.Text.Length - 1) == "*" ||
this.textBox1.Text.Substring(this.textBox1.Text.Length - 1) == "/")
{
}
else
{
this.textBox1.Text += this.button_mul.Text;
calculate_symbol[symbol_index] = "*";
symbol_index++;
}
}
}
private void button_result_Click(object sender, EventArgs e)
{
string s = textBox1.Text;
if (s[0] == '-')
{
s = '0' + s;
}
//如果表达式结尾是运算符号或者小数点,直接忽略
if (s[s.Length - 1] == '+' || s[s.Length - 1] == '-' || s[s.Length - 1] == '*' || s[s.Length - 1] == '/' || s[s.Length - 1] == '.')
{
s = s.Remove(s.Length - 1, 1);
}
Stack<double> numstack = new Stack<double>();//数字栈
Stack<char> symstack = new Stack<char>();//符号栈
//遍历字符串,截取操作数和运算符,分别入栈来计算
for (int i = 0; i < s.Length; i++)
{
//不是符号就继续
if (s[i] != '+' && s[i] != '-' && s[i] != '*' && s[i] != '/')
{
//继续遍历来判断操作数有多长
for (int j = i; j < s.Length; j++)
{
//遇到符号,遍历停止
if (s[j] == '+' || s[j] == '-' || s[j] == '*' || s[j] == '/')
{
//将操作数存进数字栈
numstack.Push(float.Parse(s.Substring(i, j - i)));
//遍历结束
i = j - 1;
break;
}
//遍历到最后一位
if(j == s.Length - 1)
{
//最后一个操作数直接压栈
numstack.Push(double.Parse(s.Substring(i, j + 1 - i)));
//遍历结束
i = s.Length;
}
}
}
//遍历到运算符
else
{
//符号栈为空,直接入栈
//第一个运算符直接入栈
if (symstack.Count == 0)
{
symstack.Push(s[i]);
}
//后续进行优先级判断
//准备入栈元素优先级低于或者等于栈顶元素,先从栈里面拿出两个运算数和一个符号计算后方可入栈
else if (((s[i] == '+' || s[i] == '-') && (symstack.Peek() == '*' || symstack.Peek() == '/'))
|| ((s[i] == '+' || s[i] == '-') && (symstack.Peek() == '+' || symstack.Peek() == '-'))
|| ((s[i] == '*' || s[i] == '/') && (symstack.Peek() == '*' || symstack.Peek() == '/')))
{
//拿出数字栈最上面两个操作数
double a1 = numstack.Pop();
double b1 = numstack.Pop();
//定义当前运算符为当前栈顶运算符
char op1 = symstack.Peek();
switch (op1)
{
case '+':
{
numstack.Push(b1 + a1);
break;
}
case '-':
{
numstack.Push(b1 - a1);
break;
}
case '*':
{
//结果入数字栈
numstack.Push(b1 * a1);
break;
}
case '/':
{
numstack.Push(b1 / a1);
break;
}
}
//运算完毕,丢弃之前的栈顶运算符,存入新的
symstack.Pop();
symstack.Push(s[i]);
}
//其他情况不涉及优先级,直接入栈
else
{
symstack.Push(s[i]);
}
}
}
//循环结束
//操作数和运算符分别入栈,准备计算
while (symstack.Count != 0)
{
char op2 = symstack.Pop();
double a = numstack.Pop();
double b = numstack.Pop();
switch (op2)
{
case '+':
{
numstack.Push(a + b);
break;
}
case '-':
{
numstack.Push(b - a);
break;
}
case '*':
{
//结果入数字栈
numstack.Push(a * b);
break;
}
case '/':
{
numstack.Push(b / a);
break;
}
}
}
//输出结果
s = numstack.Peek().ToString("f1");
textBox1.Text += "=";
textBox1.Text += s;
}
private void button2_Click(object sender, EventArgs e)
{
//2
if (this.textBox1.Text.Contains("="))
{
}
else
{
this.textBox1.Text += this.button2.Text;
}
}
private void button3_Click(object sender, EventArgs e)
{
//3
if (this.textBox1.Text.Contains("="))
{
}
else
{
this.textBox1.Text += this.button3.Text;
}
}
private void button4_Click(object sender, EventArgs e)
{
//4
if (this.textBox1.Text.Contains("="))
{
}
else
{
this.textBox1.Text += this.button4.Text;
}
}
private void button5_Click(object sender, EventArgs e)
{
//5
if (this.textBox1.Text.Contains("="))
{
}
else
{
this.textBox1.Text += this.button5.Text;
}
}
private void button6_Click(object sender, EventArgs e)
{
//6
if (this.textBox1.Text.Contains("="))
{
}
else
{
this.textBox1.Text += this.button6.Text;
}
}
private void button7_Click(object sender, EventArgs e)
{
//7
if (this.textBox1.Text.Contains("="))
{
}
else
{
this.textBox1.Text += this.button7.Text;
}
}
private void button8_Click(object sender, EventArgs e)
{
//8
if (this.textBox1.Text.Contains("="))
{
}
else
{
this.textBox1.Text += this.button8.Text;
}
}
private void button0_Click(object sender, EventArgs e)
{
//0
if (this.textBox1.Text.Contains("="))
{
}
else
{
this.textBox1.Text += this.button0.Text;
}
}
private void Form1_Load(object sender, EventArgs e)
{
//MessageBox.Show("请使用鼠标点击!");
}
private void button_point_Click(object sender, EventArgs e)
{
//.
if (this.textBox1.Text.Contains("="))
{
}
else
{
this.textBox1.Text += this.button_point.Text;
}
}
}
}