做个计算器--制作计算器

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: 做个计算器--制作计算器

从这篇文章开始,我们开始进入到代码实操阶段。在这一阶段,首先开发一个简单的计算器,接着学习如何获取计算机的硬件信息(cpu编号、硬盘编号和主板编号等),然后利用获得到的硬件信息(主要是硬件编号)来实现注册码的生成,最后我们就要完整的实现一机一码的功能,并且讲解如何防止共享机器码。下面就开始这个阶段的第一篇文章。


零、设计软件界面

我们设计的软件一共有两个,一个是用户使用的计算器,它包含两个 Form 窗口 软件注册窗口 和 计算器窗口 ,其中软件注册窗口主要是提供机器码和注册软件功能。另一个软件是软件开发商所使用的注册机,通过它开发商可以利用机器码生成注册码。下面我们分别来看一下计算器和注册机界面的设计。


0.1 计算器界面

首先我们来看一下注册界面。界面很简单,包含一个机器码显示框,一个注册码输入框和一个复制机器码的按钮以及注册软件的按钮。这个窗口的具体代码实现我会在后面的文章中进行讲解。

image.png

接下来我们再来看一下计算器界面。这其实就是一个简单的计算器,可以通过 Menu 下拉按钮来选择是科学计算器还是标准计算器。

image.png

这个项目的目录结构如下,

image.png

Data 目录中放置了项目要用到的实体类,并且该实体类继承 INotifyPropertyChanged 接口,当属性发生变化时,变化后的数据会直接显示在界面中。

Method 目录存放了我们的软件实现科学计算器和标准计算器功能的方法。

View 目录是自定义控件目录,用于在我们选择不同类型计算器时显示不同的界面。

MainForm 是计算器的主窗体。

Registerd 是注册窗体。


0.2 注册机界面

我们再来看一下注册机界面。注册机界面跟计算器的注册窗体很像,只不过就是将复制机器码按钮改为了复制注册码按钮,注册按钮改为了生成注册码按钮。注册记得代码我将在后续文章中逐步讲解。

image.png

注册机项目的目录结构如下。

image.png

一、计算器代码编写

这里我们知道计算器的核心代码,代码中已经写好备注。

using System;
using System.Collections;
namespace Calculator.Method
{
  /// <summary>
  /// 将中缀表达式翻译成后缀表达式
  /// 输入中缀表达式: A+B*(C+D)-E/F
  /// 翻译成后缀表达式:ABCD+*+EF/-
  /// 中缀表达式翻译成后缀表达式的方法如下:
  /// (1)从左向右依次取得数据ch
    /// (2)如果ch是操作数,直接输出
  /// (3)如果ch是运算符(含左右括号),则:
  /// a:如果ch = "(",放入堆栈
  /// b:如果ch = ")",依次输出堆栈中的运算符, 直到遇到"("为止
  /// c:如果ch不是")"或者"(",那么就和堆栈顶点 位置的运算符top做优先级比较
  /// 1:如果ch优先级比top高,那么 将ch放入堆栈
  /// 2:如果ch优先级低于或者等于 top,那么输出top,然后将ch放入堆栈
  /// (4)如果表达式已经读取完成,而堆栈中还有运算符时,依次由顶端输出
  /// </summary>
  public class AnalyExpression
  {
    //声明表达式并赋初值
    public static string expression;
    //声明堆栈
    private static Stack myStack = new Stack();
    //作为主函数,供外界调用,传入参数为表达式,输出参数为解析并计算表达式之后的结果
    public static void AnalyExpressions(string exp, out double res)
    {
      //将表达式赋值为传进来的参数exp
      expression = exp;
      //对B初始化,每一个索引全部赋值为空
      for (int i = 0; i < B.Length; i++)
      {
        B[i] = null;
      }
      //调用解析方法,将中缀表达式解析为类似的“后缀表达式”
      Parse();
      //调用计算方法,对“后缀表达式”进行计算,返回计算结果
      res = Calculate();
    }
    //声明数组,将中缀表达式解析之后放入此数组中
    static string[] B = new string[100];
    //标记一个多位数字是否为小数(带小数点)
    static bool haspoint;
    //标记小数位的位数,从1开始计数
    static int figure = 1;
    //将中缀表达式解析成后缀表达式
    public static void Parse()
    {
      int i, j = 0;
      string ch, ch1;
      char[] A = expression.ToCharArray(); //将字符串转成字符数组
      int length = A.Length;
      int index = 1;  //记录当前数字的索引
      for (i = 0; i < length; i++)
      {
        ch = A[i] + "";     //对字符之后添加一个空引号,以隐式转换为字符串
                  //强制转换字符char为字符串string会转换为对应的ASCII码
        if (IsOperand(ch)) //如果是操作数,直接放入B中
        {
          //中缀表达式被分解为字符数组,因此在支持两位以上的数字时
          //首先记录一个数字的索引,假如第二个数字的索引与第一个数字的索引之差为1
          //则说明两个数字应组成为一个多位数
          if (index == i - 1)
          {
            index = i;
            //如果当前字符为小数点,则在数组B上一个索引后追加“.0”,并标记当前数据为小数
            if (ch == ".")
            {
              B[--j] = B[j] + ".0";
              haspoint = true;
            }
            //如果当前数据为小数,则之后的字符追加到此数据之后,按照以下规则
            else if (haspoint)
            {
              B[--j] = (double.Parse(B[j])) + Convert.ToDouble(ch.ToString()) * (1 / (Math.Pow(10, figure))) + "";
              figure++;
            }
            //当前数据不是小数,追加到此数据之后,按照以下规则
            else
            {
              B[--j] = (double.Parse(B[j]) * 10 + Convert.ToDouble(ch.ToString())) + "";
            }
            ++j;
          }
          else
          {
            index = i;      //记录当前数字的索引
            B[j++] = ch + "";
          }
        }
        else
        {
          if (ch == "(") //如果是“(”,将它放入堆栈中
            myStack.Push(ch);
          else if (ch == ")") //如果是“)”
          {
            while (!IsEmpty(myStack)) //不停地弹出堆栈中的内容,直到遇到“(”
            {
              ch = (string)myStack.Pop();
              if (ch == "(")
                break;
              else
                B[j++] = ch + ""; //将堆栈中弹出的内容放入B中
            }
          }
          else //既不是“(”,也不是“)”,是其它操作符,比如 +, -, *, / 之类的
          {
            if (!IsEmpty(myStack))
            {
              do
              {
                ch1 = (string)myStack.Pop();//弹出栈顶元素
                if (Priority(ch) > Priority(ch1)) //如果栈顶元素的优先级小于读取到的操作符
                {
                  myStack.Push(ch1);//将栈顶元素放回堆栈
                  myStack.Push(ch);//将读取到的操作符放回堆栈
                  break;
                }
                else//如果栈顶元素的优先级比较高或者两者相等时
                {
                  B[j++] = ch1 + ""; //将栈顶元素弹出,放入B中
                  if (IsEmpty(myStack))
                  {
                    myStack.Push(ch); //将读取到的操作符压入堆栈中
                    break;
                  }
                }
              } while (!IsEmpty(myStack));
            }
            else //如果堆栈为空,就把操作符放入堆栈中
            {
              myStack.Push(ch);
            }
          }
        }
      }
      while (!IsEmpty(myStack))
      {
        B[j++] = myStack.Pop() + "";//将堆栈中剩下的操作符输出到B中
      }
    }
    //计算“后缀表达式”的值
    public static double Calculate()
    {
      int i;
      double no1, no2, ret;
      string ch;
      //把B中的null值去掉
      int n = 0;
      for (; n < B.Length; n++)
      {
        if (B[n] == null)
        {
          break;
        }
      }
      string[] A = new string[n];
      //将B的非null数据复制到A中
      for (n = 0; n < A.Length; n++)
      {
        A[n] = B[n];
      }
      myStack.Clear();
      for (i = 0; i < A.Length; i++)
      {
        ch = A[i];
        if (IsOperand(ch))//如果是操作数,直接 压入栈
        {
          myStack.Push(double.Parse(ch));
        }
        else //如果是操作符,就弹出两个数字来进行运算
        {
          no1 = (double)myStack.Pop();
          no2 = (double)myStack.Pop();
          ret = GetValue(ch, no1, no2);
          myStack.Push(ret);//将结果压入栈
        }
      }
      return (double)myStack.Pop();//弹出最后的运算结果
    }
    //对两个值利用运算符计算结果
    private static double GetValue(string op, double ch1, double ch2)
    {
      switch (op)
      {
        case "+":
          return ch2 + ch1;
        case "-":
          return ch2 - ch1;
        case "*":
          return ch2 * ch1;
        case "/":
          return ch2 / ch1;
        default:
          return 0;
      }
    }
    //判断堆栈是否为空
    private static bool IsEmpty(Stack st)
    {
      return st.Count == 0 ? true : false;
    }
    //判断是否是操作数
    private static bool IsOperand(string ch)
    {
      string[] operators = { "+", "-", "*", "/", "(", ")" };
      for (int i = 0; i < operators.Length; i++)
        if (ch == operators[i])
          return false;
      return true;
    }
    //返回运算符的优先级
    private static int Priority(string ch)
    {
      int priority;
      switch (ch)
      {
        case "+":
          priority = 1;
          break;
        case "-":
          priority = 1;
          break;
        case "*":
          priority = 2;
          break;
        case "/":
          priority = 2;
          break;
        default:
          priority = 0;
          break;
      }
      return priority;
    }
  }
}
using System;
namespace Calculator.Method
{
  public class PrintAndExpression
  {
    //获取控件名称,返回数字或者操作符
    public string GetValue(string name)
    {
      switch (name)
      {
        case "one":
          return "1";
        case "two":
          return "2";
        case "three":
          return "3";
        case "four":
          return "4";
        case "five":
          return "5";
        case "six":
          return "6";
        case "senven":
          return "7";
        case "eight":
          return "8";
        case "nine":
          return "9";
        case "zero":
          return "0";
        case "point":
          return ".";
        case "mod":
          return "Mod";
        case "radication":
          return "√";
        case "square":
          return "x²";
        case "daoshu":
          return "1⁄x";
        case "CE":
          return "CE";
        case "C":
          return "C";
        case "delete":
          return "delete";
        case "div":
          return "/";
        case "multiply":
          return "*";
        case "sub":
          return "-";
        case "add":
          return "+";
        case "minus":
          return "±";
        case "mi":
          return "x^y";
        case "sin":
          return "sin";
        case "tan":
          return "tan";
        case "cos":
          return "cos";
        case "shimi":
          return "10^x";
        case "log":
          return "log";
        case "exp":
          return "exp";
        case "pi":
          return "3.14";
        case "fact":
          return "n!";
        case "left":
          return "(";
        case "right":
          return ")";
        case "per":
          return "%";
        case "equal_sign":
          return "=";
        default:
          return "";
      }
    }
    //判断是操作符还是数字,一元操作符还是二元操作符
    //数字,小数点,pi及正负号返回0
    //一元操作符返回1
    //二元操作符返回2
    //否则返回-1
    public int Is0peration(string name)
    {
      string[] Digital = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", ".", "3.14" };
      string[] UnaryOperators = { "Mod", "√", "x²", "1⁄x", "±", "sin", "cos", "tan", "10^x", "log", "exp", "n!" };
      string[] BinaryOperators = { "/", "*", "-", "+", "(", ")" };
      foreach (string item in Digital)
      {
        if (GetValue(name) == item)
        {
          return 0;               //数字
        }
      }
      foreach (string item in UnaryOperators)
      {
        if (GetValue(name) == item)
        {
          return 1;               //一元运算符
        }
      }
      foreach (string item in BinaryOperators)
      {
        if (GetValue(name) == item)
        {
          return 2;               //二元运算符
        }
      }
      return -1;
    }
    //求一个数的阶乘,并返回结果
    public float Fact(string number1)
    {
      float n = float.Parse(number1);
      float i = n;
      for (; n > 1; n--)
      {
        i = i * n - 1;
      }
      return i;
    }
    //保存表达式
    public static string expression;
    public void PrintText(string name, string GetText1, string GetTxet2, out string SetText1, out string SetText2)
    {
      //每按下一个按钮,将按钮所表示的含义打印到TextBlock中
      //如果为数字(包括多位数),暂时保存在SetText1中。即下方那一栏文本框
      //如果为一元运算符,将GetText1中的数字连同一元运算符一并保存到SetText2中,表达式直接获得一元运算符计算结果
      //如果为二元运算符,将GetText1中的数字连同二元运算符一并保存到SetText2中。
      //判断str1是否含有等号。和算术表达式异常提示信息。则先清空。
      if (GetText1 != null && GetText1.Contains("="))
      {
        GetTxet2 = null;
        GetText1 = null;
      }
      else if (GetText1 == "你的算式无法计算" || GetText1 == "除数不能为零" || GetText1 == "输入无效")
      {
        GetTxet2 = null;
        GetText1 = null;
      }
      //是否为操作符,数字
      int isOperation = Is0peration(name);
      if (isOperation == 0)       //数字
      {
        if(GetText1=="0")
        {
          GetText1 = "";
        }
        SetText2 = GetTxet2;
        SetText1 = GetText1 + GetValue(name);     //显示
        //expression = GetTxet2;
      }
      else if (isOperation == 1)  //一元运算符
      {
        SetText1 = GetText1;
        //对于一元运算符直接调用Math的方法计算出结果
        try
        {
          switch (name)
          {
            case "square":
              SetText2 = GetTxet2 + "(" + GetText1 + ")²";
              expression = expression + float.Parse(GetText1) * float.Parse(GetText1) + "";
              break;
            case "sin":
              SetText2 = GetTxet2 + GetValue(name) + "(" + GetText1 + ")";
              expression = expression + Math.Sin(float.Parse(GetText1)) + "";
              break;
            case "cos":
              SetText2 = GetTxet2 + GetValue(name) + "(" + GetText1 + ")";
              expression = expression + Math.Cos(float.Parse(GetText1)) + "";
              break;
            case "tan":
              SetText2 = GetTxet2 + GetValue(name) + "(" + GetText1 + ")";
              expression = expression + Math.Tan(float.Parse(GetText1)) + "";
              break;
            case "radication":
              SetText2 = GetTxet2 + GetValue(name) + "(" + GetText1 + ")";
              expression = expression + Math.Sqrt(float.Parse(GetText1)) + "";
              break;
            case "shimi":
              SetText2 = GetTxet2 + "10^" + GetText1;
              expression = expression + Math.Pow(10, float.Parse(GetText1)) + "";
              break;
            case "fact":
              SetText2 = GetTxet2 + GetText1 + "!";
              expression = expression + Fact(GetText1) + "";
              break;
            case "minus":                         //负号
              SetText2 = GetTxet2 + "(" + "-" + GetText1 + ")";
              expression = expression + "(0" + "-" + GetText1 + ")";
              break;
            case "daoshu":
              SetText2 = GetTxet2 + "1⁄" + GetText1;
              if (0 == int.Parse(GetText1))
              {
                SetText1 = "除数不能为零";
                break;
              }
              else
              {
                expression = expression + 1 / float.Parse(GetText1) + "";
              }
              break;
            case "exp":
              SetText2 = GetTxet2 + "exp(" + GetText1 + ")";
              expression = expression + Math.Exp(float.Parse(GetText1)) + "";
              break;
            case "log":
              SetText2 = GetTxet2 + "log(" + GetText1 + ")";
              if (0 == int.Parse(GetText1))
              {
                SetText1 = "输入无效";
                break;
              }
              else
              {
                expression = expression + Math.Log(float.Parse(GetText1)) + "";
              }
              break;
            default:
              SetText2 = GetTxet2;
              SetText1 = "计算错误";
              break;
          }
          SetText1 = "";
          GetText1 = "";
        }
        catch (Exception e)
        {
          SetText1 = "输入无效";
          SetText2 = "";
        }
      }
      else if (isOperation == 2)  //二元运算符
      {
        //如果运算符为左括号
        if (name == "left")
        {
          SetText2 = GetTxet2 + GetValue(name);
          SetText1 = "";
          expression = expression + GetValue(name);
        }
        //如果运算符为右括号
        else if (name == "right")
        {
          SetText2 = GetTxet2 + GetText1 + GetValue(name);
          SetText1 = "";
          expression = expression + GetText1 + GetValue(name);
        }
        else
        {
          SetText2 = GetTxet2 + GetText1 + GetValue(name);    //显示
          SetText1 = "";
          expression = expression + GetText1 + GetValue(name);
        }
      }
      else if (GetValue(name) == "delete")
      {
        //将Text1中的数依次删除
        SetText2 = GetTxet2;
        if(GetText1!="0"&&GetText1!=null&&GetText1!="")
        {
          SetText1 = GetText1.Remove(GetText1.Length - 1, 1);
        }
        else if(GetText1==null)
        {
          SetText1 = "0";
        }
        else
        {
          SetText1 = GetText1;
        }
        expression = GetTxet2;
      }
      else if (GetValue(name) == "CE")
      {
        //将Text1中的数清空
        SetText2 = GetTxet2;
        SetText1 = "0";
        expression = GetTxet2;
      }
      else if (GetValue(name) == "C")
      {
        //清空所有内容
        SetText2 = "";
        SetText1 = "0";
        expression = null;
      }
      else if (GetValue(name) == "=")
      {
        SetText2 = GetTxet2 + GetText1;
        expression = expression + GetText1;
        if (expression == null)
        {
          SetText1 = "";
        }
        else
        {
          //添加一个等号,标记为计算结果。再输入数字的时候应该先清空。
          //SetText1 = "=" + Caculate();
          string result = Caculate();
          SetText1 = result == "error" ? "你的算式无法计算" : "=" + result;
        }
      }
      else
      {
        SetText1 = "error";
        SetText2 = "error";
      }
    }
    public string Caculate()
    {
      string str = expression;
      //调用算术表达式解析算法 
      double result = 0;
      //捕获异常
      try
      {
        AnalyExpression.AnalyExpressions(str, out result);
      }
      catch (Exception e)
      {
        result = 0;
        return "error"; //出现异常返回error
      }
      return result + "";
    }
  }
}

二、注册界面跳转

当我们注册成功后,就需要跳转到功能窗体去。对于这个跳转功能有两种方法,一种是隐藏注册窗体,显示功能窗体,另一种是采用 DialogResult 来实现,下面我们分别来看一下这两种方法的代码如何编写。

方法一,在注册按钮的 click 事件上上编写如下代码:

this.Visible=false;//隐藏当前窗体
MainForm mf = new MainForm();
mf.Show();//显示功能窗体。

方法二,将程序入口点文件 Program 文件的 Main 方修改如下:

Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
//先显示窗体Registerd
Registerd r=new Registerd();
if(r.ShowDialog()==DialogResult.OK)//当窗体Registerd关闭时再打开MainForm船体
{
    Application.Run(new MainForm());
}

接着在 Registerd 窗体的注册按钮的 click 事件中增加如下代码:

this.DialogResult=DialogResult.OK;

以上两种方法都可以使用,各位读者可以根据自己的习惯来选择,但是我还是推荐使用方法二,因为在企业级桌面项目中方法二的实现不消耗过多的资源。


三、总结

本篇文章主要展示并讲解了计算器和注册机界面的设计,以及计算器功能的简单实现,并讲解了如何实现注册界面跳转到功能界面。本阶段后面的文章将以这篇文章为基础去逐步完善计算机的功能,并实现一机一码功能。


Tip:

本文计算器代码地址:https://gitee.com/miaoshu_studio/calculator.git

本文注册机代码地址:https://gitee.com/miaoshu_studio/RegisterMachine.git


目录
相关文章
|
23天前
|
C++
每周一坑--简单的计算器
每周一坑--简单的计算器
|
2月前
计算器V2
编写了一个简单的程序,实现了浮点数的加、减、乘、除和幂运算。程序包括了对浮点数的计算,并展示了运算结果。其中,幂运算需包含&quot;math.h&quot;头文件。
23 0
|
2月前
计算器V1
创建一个简单的计算器程序,能执行整数的加、减、乘、除和求余运算。用户输入格式为:操作数1 运算符op 操作数2。遇到除数为0时,输出&quot;Division by zero!&quot;;运算符非法则输出&quot;Invalid operator!&quot;。示例输入和输出已给出。
32 0
|
2月前
|
前端开发 JavaScript
使用html+css+javaScript 完成计算器
使用html+css+javaScript 完成计算器
|
12月前
|
Java
从计算器小例子的总结思考
从计算器小例子的总结思考
102 0
|
2月前
|
前端开发
好看的前端计算器代码分享(html+css+js制作计算器)
好看的前端计算器代码分享(html+css+js制作计算器)
52 0
|
10月前
一个计算器器脚本
一个计算器器脚本
44 1
|
Java 数据安全/隐私保护 容器
背记不如实战系列-javaGUI实例-计算器制作
背记不如实战系列-javaGUI实例-计算器制作
81 0
|
算法
lougu 2485计算器(BSGS)
lougu 2485计算器(BSGS)
116 0