#yyds干货盘点#制作简易计算器
Posted 494324190
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了#yyds干货盘点#制作简易计算器相关的知识,希望对你有一定的参考价值。
今天确实没什么可写的。就来搞一个小的实战项目吧:带有注册功能的简易计算器。
零、设计软件界面
我们设计的软件一共有两个,一个是用户使用的计算器,它包含两个 Form 窗口 软件注册窗口 和 计算器窗口 ,其中软件注册窗口主要是提供机器码和注册软件功能。另一个软件是软件开发商所使用的注册机,通过它开发商可以利用机器码生成注册码。下面我们分别来看一下计算器和注册机界面的设计。
0.1 计算器界面
首先我们来看一下注册界面。界面很简单,包含一个机器码显示框,一个注册码输入框和一个复制机器码的按钮以及注册软件的按钮。这个窗口的具体代码实现我会在后面的文章中进行讲解。
接下来我们再来看一下计算器界面。这其实就是一个简单的计算器,可以通过 Menu 下拉按钮来选择是科学计算器还是标准计算器。
这个项目的目录结构如下,
Data 目录中放置了项目要用到的实体类,并且该实体类继承 INotifyPropertyChanged 接口,当属性发生变化时,变化后的数据会直接显示在界面中。
Method 目录存放了我们的软件实现科学计算器和标准计算器功能的方法。
View 目录是自定义控件目录,用于在我们选择不同类型计算器时显示不同的界面。
MainForm 是计算器的主窗体。
Registerd 是注册窗体。
0.2 注册机界面
我们再来看一下注册机界面。注册机界面跟计算器的注册窗体很像,只不过就是将复制机器码按钮改为了复制注册码按钮,注册按钮改为了生成注册码按钮。注册记得代码我将在后续文章中逐步讲解。
注册机项目的目录结构如下。
一、计算器代码编写
这里我们知道计算器的核心代码,代码中已经写好备注。
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;
以上两种方法都可以使用,各位读者可以根据自己的习惯来选择,但是我还是推荐使用方法二,因为在企业级桌面项目中方法二的实现不消耗过多的资源。
三、总结
本篇文章主要展示并讲解了计算器和注册机界面的设计,以及计算器功能的简单实现,并讲解了如何实现注册界面跳转到功能界面。本阶段后面的文章将以这篇文章为基础去逐步完善计算机的功能,并实现一机一码功能。
以上是关于#yyds干货盘点#制作简易计算器的主要内容,如果未能解决你的问题,请参考以下文章
#yyds干货盘点#Prometheus 之 PromQL 介绍