基于WinForm构建的多项式计算器

  1. 控件的设置

我们先依次将控件摆放好,如图:

我们用groupbox将操作栏统一整合,方便后续我们的位置调整

在groupbox里面,我们将一系列的button控件置于上方,用button来进行选择

文本框添加textbox,按钮添加button,设置好按钮属性,在闪电图标对应的事件中选择添加Click事件。

  1. 代码难点分析

我们首先来分析一下计算器的难点;

         能够识别四则运算的优先级和括号 的计算器,难点在于如何在遇到乘号,除号或者是括号的时候暂时不计算前面的加法和减法,而先去计算优先级高的运算符。

         在这里,我们使用栈这一数据结构,利用其先进后出的特性,将读到的第一个符号压入符号栈,在读到下一个符号,与栈顶的符号的优先级进行比较,若优先级比栈顶的那个符号低,就将栈顶符号的连接的两个数字(数字存在数字栈中)进行运算,否则将当前符号压入符号栈。遍历完一遍之后算式就只剩加减号了(或者直接得出答案了),然后就很简单了。

  1. 代码具体实现

我们使用两个栈结构,一个存数字,一个存运算符。

       为了实现用栈计算算数表达式的值,需设置两个工作栈:用于存储运算符的栈fn,以及用于存储操作数及中间结果的栈num。

计算步骤:

定义index对表达式进行扫描; 创建两个栈,一个栈存放数字,称为数栈(num);一个栈存放符号,称为符号栈(fn);

当遇到数字时,入数栈;(注意字符位数,数字有可能是多位数)

2.1 若是字符串最后一个的时候,直接入数栈

2.2 若不是最后一个字符,需要再往后判断一位,若后一位不是符号,继续遍历,并将该字符拼接;若是则进数栈

当遇到符号时,入符号栈

   3.1如果符号栈为空,则直接入栈

   3.2如果符号栈不为空;则需要判断优先级;

       3.2.1判断优先级,若当前符号的优先级小于栈顶符号的优先级,则栈顶符号出栈,同时数栈中两个数字出栈;进行计算,得出的结果入数栈,当前符号入栈。

       3.2.2若优先级大于前一个符号,则继续进行入栈操作。

当表达式遍历完后,则两个数字、一个符号依次出栈,进行操作,最后栈中剩的最后一个数为结果

通过使用两个栈来计算表达式的值:一个存储操作数(数字),另一个存储运算符。

扫描表达式:逐字符处理表达式。

  1. 跳过空格。
  2. 如果是数字或小数点,提取整个数字并将其压入操作数栈。
  3. 如果是左括号,将其压入运算符栈。
  4. 如果是右括号,弹出运算符栈直到遇到左括号,并计算这些运算符对应的操作数。
  5. 如果是运算符,比较其与运算符栈顶运算符的优先级:
  • 若当前运算符优先级低或相等,先处理栈顶运算符,再将当前运算符压入栈。
  • 若当前运算符优先级高,直接将其压入栈。

处理剩余运算符:最后阶段,处理所有留在栈中的运算符,直到运算符栈为空。

先来给出每个控件对应的事件的实现,每个数字,我们都将其追加到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;

        }

我们来对代码进行详细分析:

  1. 初始设置
    •  textBox1 中获取输入字符串 s
    • 如果字符串以 '-'(负数)开头,会在开头添加 '0',以确保解析时一致性。
    • 移除字符串末尾的操作符或小数点,确保字符串以有效的数字表达式结尾。
  2. 堆栈初始化
    • numstack:存放数字操作数的堆栈。
    • symstack:存放操作符(+、-、*、/)的堆栈。
  3. 处理输入字符串
    • 遍历字符串 s 中的每个字符。
    • 如果字符是数字,则收集整个数字(包括小数部分),并将其压入 numstack
    • 如果字符是操作符(+、-、*、/),则利用 symstack 管理操作符的优先级:
      • 如果当前操作符比 symstack 顶部的操作符优先级更高,则使用 numstack  symstack 的顶部操作数执行计算。
      • 否则,将当前操作符推入 symstack
  4. 最终计算
    • 处理完 s 中的所有字符后,执行 symstack 中剩余的所有操作,直到 symstack 为空。这确保按正确的优先级顺序执行所有计算。
  5. 显示结果
    • 最终结果格式化为一位小数("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;
            }
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值