程序设计工具:
1、语言:JAVA
2、开发环境:IntelliJ IDEA Community Edition 2020
实验内容
-
学习图形界面的设计,利用 MFC 应用程序(Java swing 或 QT 框架,或 C#)创
建基于对话框的应用程序,添加按钮、编辑框等控件。 -
能通过设计的按钮控件输入并实现简单算术运算,要求表达式在编辑框中显示,
能将运算结果,输出在编辑框内显示;并保存历史的表达式运算记录。 -
也能够实现混合运算的算术表达式求解,算术表达式中包括加、减、乘、除、
括号等运算符;并且能够识别括号,优先级正确。
实验代码部分:
本实验主要分为图形界面设计和计算方法设计两部分
一、图形界面设计:
博主花了一天时间了解了java的GUI图形设计中主要用到的两个库分别是
import java.awt.event.*; //不可直接用java.awt.*
import javax.swing.*;
1、
以下附上介绍这两个库的博客中我参考的比较实用的三篇博客:
swing库学习的连接
awt库学习连接1
awt库学习连接2
2、首先创建一个类Calculator是对javax.swing中的JFrame类的继承java.awt.event.中 ActionListene的接口装配,继而生成构造器。
并创建容器(名为frame)。
public class Calculator extends JFrame implements ActionListener { //Calculator继承JFrame并实现JAVA.awt.event库中的ActionListener的接口;
Calculator() {
init(); //调用实例构造器,(从父类的变量及方法到子类的变量及方法的构造);
}
//实例构造器的定义
public void init() {
JFrame frame = new JFrame ("The Calculator Of Czf"); //普通窗口顶层容器 创建标题为“计算器”;
frame.setLayout(null);
3、按键的设置:按键的排版以及设计需要提前考虑整体的布局合理性,不可出现按键重叠;为了保证美观又需要考虑排版整齐。
setBounds含有四个参数: 设置窗口左上角位于屏幕左上角(左顶点为(x,y)),宽为width像素,高为high像素。
注:在给按键命名时要根据按键的大小,否则按键上的按键名称无法完全显示;
(按键上会显示三个小点点,表示按键名称无法完全显示);
//放置数字0
button0 = new JButton("0");
button0.setBounds(150, 400, 90, 50);
frame.add(button0);
后面是依葫芦画瓢把你需要的按键全部排版在窗口中。
4、java.awt.event.ActionListener调用监视器的方法,addActionListener()必须有事件监听对象 this表示当前类的对象,代码中会生成一个默认参数引导(我猜是这么解读)自动生成的|:不知道是个啥,反正不用管就行了。
//java.awt.event.ActionListener调用监视器的方法
buttonhistoricalrecords.addActionListener(this); //addActionListener()必须有事件监听对象 this表示当前类的对象
button0.addActionListener(this);
后面依旧是把之前设置的所有按键都按照这种格式增加一个监视器,来监视按键的状态;
5、 //通过JButton 定义各个按键对象;(全部按键名都需要定义)举例如下:
JButton buttonhistoricalrecords;
JButton button0;
JButton button1;
6、用来做标志位的变量定义:
相关的解释在代码中我都做了注解;
String s = null;
int pointbook = 0; //记录小数点是否出现,每次当前数字框中只能出现一个小数点;
int equalbook = 0; //记录等号是否出现,每次计算的总算式只能出现一个等号;
int leftNum = 0; //记录左括号的数量;
int rightNum = 0; //记录有括号的数量;
Stack<Double> history = new Stack<Double>();//用以保存历史答案;
二、 算法部分:
7、以下是对从父类继承来的方法的重写(主要就是对监视器的按键反应的操作)
按照不同按键我们期望获取的反应来编写我们的程序;
具体每一步以及方法的相关信息都在注解中;
从0-9按键的内容几乎一致,只要考虑每个按键的对应细节就行。
下面是对按键0的操作:
注:数字输入要判断是否超出8,避免数值溢出无法计算。
@Override
public void actionPerformed(ActionEvent e) { //创建awt.event库下ActionEvent类的对象e;
//当按钮为0时
if(e.getSource().equals(button0)) { //通过对象获取当前按键信息判断为哪一个按键;
s=numberText.getText(); //数字文本框调用getText获取已经输入内容;
if(s.length()>8 || s.equals("0") || equalbook == 1) { //已有操作数长度判断:长度超过8,或现有操作数为0 或已有等号不执行输入0的操作;
}
else {
numberText.setText(s + "0"); //符合条件则向现有操作数中添加0 ;
}
}
8、对于功能按键;
功能按键主要就是对一些标志位的操作和文本框的信息显示或清空;
//当按下C键时;
if(e.getSource().equals(buttonC)) {
numberText.setText("0"); //置当前数字框为0;
expressText.setText(""); //置空当前表达式框;
leftNum = 0; //当一次计算完成之后,只有按CE按钮才能进行新的计算,因为要清除所有标志位否则会影响下一次操作;
rightNum = 0;
pointbook = 0;
equalbook = 0;
}
//当按钮为CE时,
if(e.getSource().equals(buttonCE)) {
numberText.setText("0"); //清除当前数字框中内容;
pointbook = 0; //更新小数点状态为0;
}
我在本计算器中增添了一个历史记录(按键名为H)利用栈存储历史结果信息,可以倒叙查询前1次的计算结果;(不过出了点问题,跑不起来这个功能就没搞太多花里胡哨)。
代码如下:
if(e.getSource().equals(buttonhistoricalrecords)) {
expressText.setText("");
expressText.setText( String.valueOf(history.peek()));
}
运算符按键的设置:
在功能按键中有:加+ , 减- ,乘 * , 除\ ,小数点 . ,左右括号( ,)。
9、小数点的设置:
- 小数点不应出现在最后一位;
- 各个运算符或者字符都应该出现在=号前,就是等号标志位不为1;
- 输入数并不是和运算符一齐输入表达式框的,而是运算符触发了操作,所以后面要特地向表达式文本框中增添运算符。
if(e.getSource().equals(buttonpoint)) {
s=numberText.getText();
if(s.length()>7 || equalbook == 1) { //小数点位于操作数的最后一位不执行操作;
}
if(pointbook==0) { //一个操作数仅能有一个小数点,若已经有小数点则不再添加小数点;
numberText.setText(s + ".");
pointbook = 1; //小数点判断位置1;
}
}
10、加+ , 减- ,乘 * , 除\ 的操作很相似:
- 判断运算符前是否有操作数,或者运算符前是否有小数点;
- 重要的事情说两遍:这里解释以下为什么s没有提取到数字框里的符号,因为输入符号时并没有更新数字框,而是直接执行一系列操作,数字框从未出现过运算符;
- 如下是加法的程序片段:(减- ,乘 * , 除\ 于此操作类似)
//当按钮为加号时
if(e.getSource().equals(buttonadd)) {
s=numberText.getText(); //获取运算符前一个操作数;
char ch1[] = s.toCharArray(); //把第一操作数连同+号 进行字符串转字符数组的操作 赋予ch1;
int length1 = s.length() - 1; //length1获取除+号外的第一操作数的位数;
String S = expressText.getText(); //获取当前表达式文本框里的已有表达式部分;
char ch2[] = S.toCharArray(); //用以与新增的数合并来更新表达式文本框;
int length2 = S.length() - 1; //ch2[length2]获取前一个操作数的最后一位,来规避运算符,
if((length2 == -1 ||ch2[length2] != ')') && (s.equals("0") || s.equals("") || ch1[length1] == '.')) {
//当数字为空或为0(操作无意义);或数字的最后一位是小数点(未输入完毕或输入出错,等待)
}
else {
numberText.setText(""); //数字文本框置空
expressText.setText(expressText.getText() + s + "+"); //合并现有表达式和新增表达式
// 这里解释以下为什么s没有提取到数字框里的符号,因为输入符号时并没有更新数字框,而是直接执行一系列操作,数字框从未出现过运算符;
}
}
11、左括号的添加:
- 括号左边是否有数或符号类别的判断,如果没有符号要么是不合法表达式要不就是左括号在表达式的开头。
- 注意添加左括号后需要为左括号的标志位leftNum加一,为右括号的添加作为参考。
if(e.getSource().equals(buttonleft)) {
if(!numberText.getText().equals("0") && !numberText.getText().equals("")) { //判断输入括号的前一个数字非空非零;
expressText.setText(expressText.getText() + numberText.getText()); //如果先向表达式中更新输入的数据
}
s = expressText.getText();
char ch[] = s.toCharArray();
int length = s.length() - 1;
if(length == -1 || ch[length] == '+' || ch[length] == '-' || ch[length] == '*' || ch[length] == '/' || ch[length] == '(' ) {
//括号左边是否有数或符号类别的判断;
expressText.setText(expressText.getText() + '('); //满足条件则加入左括号;
leftNum++; //左括号数加一标记;
}
}
12右括号的添加:
- 与左括号不同,右括号输入后需要对数字框置空因为左括号前执行了符号操作数字框时空的;
- 只有前面是数字的时候且左括号的数量大于右括号的数量的时候才能加右括号;
- 右括号数加一标记;
//当按钮为右括号时;
if(e.getSource().equals(buttonright)) {
if(!numberText.getText().equals("0")) {
expressText.setText(expressText.getText() + numberText.getText());
numberText.setText("");
}
s=expressText.getText();
char ch[] = s.toCharArray();
int length = s.length() - 1;
if(Character.isDigit(ch[length]) && leftNum > rightNum) {
rightNum++;
expressText.setText(expressText.getText() + ')');
}
}
13、等号的添加(最核心的算法);
参考的Stack库的学习博文:
栈主要用到的方法:
1、push(E):将item推入到栈中
2、pop() :将栈中的最顶一个item推出,并返回被推出的item
3、peek():返回栈中最顶的一个item,但不对其做任何操作
4、empty():判断该栈是否为空
5、search(Object):搜索某一个item在该栈中的位置【位置为离栈顶最近的item与栈顶间距离】
主要就是把表达式中的我们习惯的中序表达式变为后续表达式,就是逆波兰转换:
逆波兰表达式转换参考连接
-
从左到右扫描算式, 如果是数字, 计算数字的值后直接输出
-
如果是左括号, 直接加入栈
-
如果是右括号,查询栈顶是否为左括号, 如果是, 把左括号出栈,
如果不是,输出栈顶的符号, 出栈, 继续第三步, 直到找到左括号
- 如果是 +, -, *, /, 运算符,比较当前运算符和栈顶运算符的优先级,
如果该运算符的优先级大于栈顶运算符的优先级或者栈为空或者栈顶符号为括号, 把该运算符入栈,
否则, 也就是小于等于的情况, 输出栈顶的符号, 出栈.
将算式中的所有元素都处理完后即可得到该算式逆波兰表达式.
给出一个逆波兰表达式, 计算过程如下:
-
判断是数字还是运算符, 如果是数字, 求出数字的值后入栈.
-
如果是运算符,从栈中出栈两个数字, 用后出栈的数与前出栈的数根据运算符运算, 再把得到的值入栈.
主要就是利用栈把表达式文本框中的表达式存入栈中,中间设计多次的数值格式转换和提取。
由于代码较长而且我在代码中都添加了注解所以就不再赘述了。
//当按钮为等于号时 (核心算法部分)
if(e.getSource().equals(buttonequal)) {
s=numberText.getText();
if(!s.equals("0") && !s.equals("")) {
expressText.setText(expressText.getText() + s); //当前已有表达式非0非空输入等号前的操作数;
}
if(equalbook == 0) { //当等号标志位为0时才能输入等号;
numberText.setText("");
for(int i = 0; i < leftNum - rightNum; i++) {
expressText.setText(expressText.getText() + ')'); //补全右括号
}
String[] ansString = new String[100]; //声明一个100空间大小的String类型的字符串保存结果;
int Size = 0;
Stack<Character> Operater = new Stack<Character>(); //声明一个栈
Stack<Double> history = new Stack<Double>(); //用以保存历史答案;
s = expressText.getText();
char ch[] = s.toCharArray();
int length = ch.length; //获取等号前的全部表达式;转换为字符数组;计算长度;
for(int j = 0; j < length; j++) {
//当数组元素为数字时
if(ch[j] >='0' && ch[j] <= '9') { //数字情况; //遍历表达式,遇到一个数字后开始执行以下操作;
double Number = ch[j] - '0'; //对字符数组中字符向double类型的强制转换;
int point = 0; //记录小数点是否出现
//记录小数点出现后的位数
int bit = 1;
if(j==length-1) {
ansString[Size++] = String.valueOf(Number); //如果表达式中最后一个数遍历完,则直接转换格式为答案并放入ansString中;
}
for(j++; j<length; j++){
if((ch[j] < '0' || ch[j] >'9') && ch[j]!='.') {
j--;
ansString[Size++] = String.valueOf(Number); //提取除一个完整操作数添加到ansString中;
break;
}
if(ch[j] == '.') {
point = 1;
continue; //小数点检测;
}
if(ch[j] >= '0' && ch[j] <= '9') { //如果为同一数字的不同位数执行以下操作;
if(point == 0) {
Number = Number * 10 + (ch[j] - '0');
} else {
Number = Number + Math.pow(10, -bit) * (ch[j]-'0'); //对同一个完整数字的读取与操作;
bit++; //操作数的位数计数;
}
}
}
}
else { //符号情况;
if(ch[j] =='(') { //对左括号压栈;
Operater.push('(');
}
else if(ch[j]==')') { //右括号情况;
while(!Operater.peek().equals('(')) { //检测栈中的栈顶是否为左括号;
ansString[Size++] = String.valueOf(Operater.peek()); //将栈中除左括号后的全部内容取出保存正在ansString中;
Operater.pop(); //将栈中除左括号外的全部弹出;
}
Operater.pop(); //弹出一个栈顶的左括号;
}
else { //运算符情况;
if(Operater.empty()) { //栈空情况
Operater.push(ch[j]); //如果栈空则压如符号
}
else { //栈非空情况;
while(true) {
if(Operater.empty()) {
Operater.push(ch[j]);
break;
}
char operater1 = Operater.peek(); //从栈中提取操作运算符;
if((ch[j] == '*' || ch[j] == '/') && (operater1=='+' || operater1=='-') || (operater1=='(' || operater1 == ')')){
Operater.push(ch[j]);
break;
}
else{
ansString[Size++] = String.valueOf(Operater.peek());
Operater.pop();
}
}
}
}
}
}
while(!Operater.empty()) { //栈非空对栈中数据全部存到ansString中再弹出栈;
ansString[Size++] = String.valueOf(Operater.peek());
Operater.pop();
}
//正式计算;
Stack<Double> Last = new Stack<Double>();
for(int i=0; i<Size; i++) { //目前ansSting中存有全部操作数和操作符;
String s1 = ansString[i];
char ch2[] = s1.toCharArray();
if(ch2[0]>='0' && ch2[0]<='9') {
Last.push(Double.parseDouble(s1)); //如果ch2为数则压栈;
}
else {
double num1 = Last.pop(); //弹出两个相邻操作数;
double num2 = Last.pop();
double num3 = 0;
if(ch2[0]=='+')
num3 = num2 + num1;
else if(ch2[0]=='-')
num3 = num2 - num1;
else if(ch2[0]=='*')
num3 = num2 * num1;
else if(ch2[0]=='/')
num3 = num2 / num1;
Last.push(num3); //结果入栈;
}
}
history.push(Last.peek());
expressText.setText(expressText.getText() + "=" + Last.pop());
equalbook = 1;
}
}
if(e.getSource().equals(buttonhistoricalrecords)) {
expressText.setText("");
expressText.setText( String.valueOf(history.peek()));
}
}
14、最后就是定义一个主函数:
public static void main(String []args) {
Calculator calculator = new Calculator();
}
15、本文参考了网上许多资料以及java编写计算器的设置,心得就是,读懂别人的代码为前提,掌握基本的库里的主要方法,和中序表达式向后序表达式的转换和堆栈的算法就可以了。本文肯定存在许多不全面的地方,希望大家指正。
完整实验代码:
package First_App;
//主要采用awt组件库和swing组件库来设计图形界面
import java.awt.event.*; //不可直接用java.awt.* //ActionEvent发送事件对象,用以处理awt组件所激发的事件;
import java.util.Stack;
import javax.swing.*; //主要使用其中的几个图形设计组件;
public class Calculator extends JFrame implements ActionListener { //Calculator继承JFrame并实现JAVA.awt.event库中的ActionListener的接口;
Calculator() {
init(); //调用实例构造器,(从父类的变量及方法到子类的变量及方法的构造);
}
//实例构造器的定义
public void init() {
JFrame frame = new JFrame ("The Calculator Of Czf"); //普通窗口顶层容器 创建标题为“计算器”;
frame.setLayout(null); //设置使用特定的布局管理器;
//单选项按钮
//按键的排版以及设计需要提前考虑整体的布局合理性,不可出现按键重叠;为了保证美观又需要考虑排版整齐;
buttonhistoricalrecords = new JButton("H"); //historical records历史输入查看;JButton按钮组件创建的对象buttonhistoricalrecords;
buttonhistoricalrecords.setBounds(40, 400, 90, 50); //设置窗口左上角位于屏幕左上角(左顶点为(x,y)),宽为width像素,高为high像素;
frame.add(buttonhistoricalrecords); //通过窗口名调用add()向窗口中添加按键;
//放置数字0
button0 = new JButton("0"); //注:在给按键命名时要根据按键的大小,否则按键上的标注无法完全显示;
button0.setBounds(150, 400, 90, 50);
frame.add(button0);
//放置数字1
button1 = new JButton("1");
button1.setBounds(40, 340, 90, 50);
frame.add(button1);
//放置数字2
button2 = new JButton("2");
button2.setBounds(150, 340, 90, 50);
frame.add(button2);
//放置数字3
button3 = new JButton("3");
button3.setBounds(260, 340, 90, 50);
frame.add(button3);
//放置数字4
button4 = new JButton("4");
button4.setBounds(40, 280, 90, 50);
frame.add(button4);
//放置数字5
button5 = new JButton("5");
button5.setBounds(150, 280, 90, 50);
frame.add(button5);
//放置数字6
button6 = new JButton("6");
button6.setBounds(260, 280, 90, 50);
frame.add(button6);
//放置数字7
button7 = new JButton("7");
button7.setBounds(40, 220, 90, 50);
frame.add(button7);
//放置数字8
button8 = new JButton("8");
button8.setBounds(150, 220, 90, 50);
frame.add(button8);
//放置数字9
button9 = new JButton("9");
button9.setBounds(260, 220, 90, 50);
frame.add(button9);
//放置 .
buttonpoint = new JButton("."); //text:为自动生成的参数的解释词
buttonpoint.setBounds(260, 400, 90, 50); //自动补齐的参数解释词
frame.add(buttonpoint);
//放置 +
buttonadd = new JButton("+");
buttonadd.setBounds(370, 400, 90, 50);
frame.add(buttonadd);
//放置 -
buttonreduce = new JButton("-");
buttonreduce.setBounds(370, 340, 90, 50);
frame.add(buttonreduce);
//放置 *
buttonride = new JButton("*");
buttonride.setBounds(370, 280, 90, 50);
frame.add(buttonride);
//放置 /
buttonexcept = new JButton("/");
buttonexcept.setBounds(370, 220, 90, 50);
frame.add(buttonexcept);
//放置 =
buttonequal = new JButton("=");
buttonequal.setBounds(480, 340, 110, 110);
frame.add(buttonequal);
//计算1/x
buttoninvert = new JButton("1/x");
buttoninvert.setBounds(480, 220, 110, 110);
frame.add(buttoninvert);
//放置左括号(
buttonleft = new JButton("(");
buttonleft.setBounds(40, 160, 90, 50);
frame.add(buttonleft);
//放置右括号)
buttonright = new JButton(")");
buttonright.setBounds(150, 160, 90, 50);
frame.add(buttonright);
//放置C 消除所有输入
buttonC = new JButton("C");
buttonC.setBounds(260, 160, 150, 50);
frame.add(buttonC);
//放置CE 消除当前输入
buttonCE = new JButton("CE");
buttonCE.setBounds(440, 160, 150, 50);
frame.add(buttonCE);
//添加表达式文本框 用以输入计算公式
expressText = new JTextField(); //文本框
expressText.setBounds(40, 20, 600, 60);
frame.add(expressText);
//添加数字文本框
numberText = new JTextField("0"); //文本框
numberText.setBounds(40, 60, 600, 60);
frame.add(numberText);
//java.awt.event.ActionListener调用监视器的方法
buttonhistoricalrecords.addActionListener(this); //addActionListener()必须有事件监听对象 this表示当前类的对象
button0.addActionListener(this); //自动生成的|:不知道是个啥
button1.addActionListener(this);
button2.addActionListener(this);
button3.addActionListener(this);
button4.addActionListener(this);
button5.addActionListener(this);
button6.addActionListener(this);
button7.addActionListener(this);
button8.addActionListener(this);
button9.addActionListener(this);
buttonpoint.addActionListener(this);
buttonadd.addActionListener(this);
buttonreduce.addActionListener(this);
buttonride.addActionListener(this);
buttonexcept.addActionListener(this);
buttoninvert.addActionListener(this);
buttonequal.addActionListener(this);
buttonleft.addActionListener(this);
buttonright.addActionListener(this);
buttonC.addActionListener(this);
buttonCE.addActionListener(this);
numberText.addActionListener(this);
expressText.addActionListener(this);
//frame.setLocationRelativeTo(null); // 把窗口位置设置到屏幕中心 (不添加更灵活)跑不起来不知道什么原因
frame.setBounds(0, 0, 700, 600); //设置整个图形窗口的大小;(通过窗口名调用)
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE),当点击窗口的关闭按钮时退出程序(没有这一句,程序不会退出)
frame.setVisible(true); //显示窗口,前面创建的信息都在内存中,通过 setVisible()并设置参数为true,把内存中的窗口显示在屏幕上;
}
//通过JButton 定义各个按键对象;
JButton buttonhistoricalrecords;
JButton button0;
JButton button1;
JButton button2;
JButton button3;
JButton button4;
JButton button5;
JButton button6;
JButton button7;
JButton button8;
JButton button9;
JButton buttonpoint;
JButton buttonadd;
JButton buttonreduce;
JButton buttonride;
JButton buttonexcept;
JButton buttonequal;
JButton buttoninvert;
JButton buttonleft;
JButton buttonright;
JButton buttonC;
JButton buttonCE;
JTextField numberText;
JTextField expressText;
String s = null;
int pointbook = 0; //记录小数点是否出现,每次当前数字框中只能出现一个小数点;
int equalbook = 0; //记录等号是否出现,每次计算的总算式只能出现一个等号;
int leftNum = 0; //记录左括号的数量;
int rightNum = 0; //记录有括号的数量;
Stack<Double> history = new Stack<Double>();//用以保存历史答案;
@Override //以下是对从父类继承来的方法的重写;
public void actionPerformed(ActionEvent e) { //创建awt.event库下ActionEvent类的对象e;
//当按钮为0时
if(e.getSource().equals(button0)) { //通过对象获取当前按键信息判断为哪一个按键;
s=numberText.getText(); //数字文本框调用getText获取已经输入内容;
if(s.length()>8 || s.equals("0") || equalbook == 1) { //已有操作数长度判断:长度超过8,或现有操作数为0 或已有等号不执行输入0的操作;
}
else {
numberText.setText(s + "0"); //符合条件则向现有操作数中添加0 ;
}
}
//当按钮为1时
if(e.getSource().equals(button1)) {
s=numberText.getText(); //获取当前数字框的操作数已经输入部分
if(s.length()>8 || equalbook == 1) {
}
else if(s.equals("0") || s.equals("")) { //为0为空判断
numberText.setText("1"); //如果1是首个输入则输入1
}else {
numberText.setText(s + "1"); //如果1不是首个元素则向操作数后面的位数添加1
}
}
//当按钮为2时
if(e.getSource().equals(button2)) {
s=numberText.getText();
if(s.length()>8 || equalbook == 1) {
}
else if(s.equals("0") || s.equals("")) {
numberText.setText("2");
}else {
numberText.setText(s + "2");
}
}
//当按钮为3时
if(e.getSource().equals(button3)) {
s=numberText.getText();
if(s.length()>8 || equalbook == 1) {
}
else if(s.equals("0") || s.equals("")) {
numberText.setText("3");
}else {
numberText.setText(s + "3");
}
}
//当按钮为4时
if(e.getSource().equals(button4)) {
s=numberText.getText();
if(s.length()>8 || equalbook == 1) {
}
else if(s.equals("0") || s.equals("")) {
numberText.setText("4");
}else {
numberText.setText(s + "4");
}
}
//当按钮为5时
if(e.getSource().equals(button5)) {
s=numberText.getText();
if(s.length()>8 || equalbook == 1) {
}
else if(s.equals("0") || s.equals("")) {
numberText.setText("5");
}else {
numberText.setText(s + "5");
}
}
//当按钮为6时
if(e.getSource().equals(button6)) {
s=numberText.getText();
if(s.length()>8 || equalbook == 1) {
}
else if(s.equals("0") || s.equals("")) {
numberText.setText("6");
}else {
numberText.setText(s + "6");
}
}
//当按钮为7时
if(e.getSource().equals(button7)) {
s=numberText.getText();
if(s.length()>8 || equalbook == 1) {
}
else if(s.equals("0") || s.equals("")) {
numberText.setText("7");
}else {
numberText.setText(s + "7");
}
}
//当按钮为8时
if(e.getSource().equals(button8)) {
s=numberText.getText();
if(s.length()>8 || equalbook == 1) {
}
else if(s.equals("0") || s.equals("")) {
numberText.setText("8");
}else {
numberText.setText(s + "8");
}
}
//当按钮为9时
if(e.getSource().equals(button9)) {
s=numberText.getText();
if(s.length()>8 || equalbook == 1) {
}
else if(s.equals("0") || s.equals("")) {
numberText.setText("9");
}else {
numberText.setText(s + "9");
}
}
//当按钮为小数点时
if(e.getSource().equals(buttonpoint)) {
s=numberText.getText();
if(s.length()>7 || equalbook == 1) { //小数点位于操作数的最后一位不执行操作;
}
if(pointbook==0) { //一个操作数仅能有一个小数点,若已经有小数点则不再添加小数点;
numberText.setText(s + ".");
pointbook = 1; //小数点判断位置1;
}
}
//每次输入都是一个数字+运算符,一起从数字文本框转而输入到表达式文本框中;
//当按钮为加号时
if(e.getSource().equals(buttonadd)) {
s=numberText.getText(); //获取运算符前一个操作数;
char ch1[] = s.toCharArray(); //把第一操作数连同+号 进行字符串转字符数组的操作 赋予ch1;
int length1 = s.length() - 1; //length1获取除+号外的第一操作数的位数;
String S = expressText.getText(); //获取当前表达式文本框里的已有表达式部分;
char ch2[] = S.toCharArray(); //用以与新增的数合并来更新表达式文本框;
int length2 = S.length() - 1; //ch2[length2]获取前一个操作数的最后一位,来规避运算符,
if((length2 == -1 ||ch2[length2] != ')') && (s.equals("0") || s.equals("") || ch1[length1] == '.')) {
//当数字为空或为0(操作无意义);或数字的最后一位是小数点(未输入完毕或输入出错,等待)
}
else {
numberText.setText(""); //数字文本框置空
expressText.setText(expressText.getText() + s + "+"); //合并现有表达式和新增表达式
// 这里解释以下为什么s没有提取到数字框里的符号,因为输入符号时并没有更新数字框,而是直接执行一系列操作,数字框从未出现过运算符;
}
}
//当按钮为减号时
if(e.getSource().equals(buttonreduce)) {
s=numberText.getText();
char ch1[] = s.toCharArray();
int length1 = s.length() - 1;
String S = expressText.getText();
char ch2[] = S.toCharArray();
int length2 = S.length() - 1;
if((length2 == -1 ||ch2[length2] != ')') && (s.equals("0") || s.equals("") || ch1[length1]=='.')) {
}
else {
numberText.setText("");
expressText.setText(expressText.getText() + s + "-");
}
}
//当按钮为乘号时
if(e.getSource().equals(buttonride)) {
s=numberText.getText();
char ch1[] = s.toCharArray();
int length1 = s.length() - 1;
String S = expressText.getText();
char ch2[] = S.toCharArray();
int length2 = S.length() - 1;
if((length2 == -1 ||ch2[length2] != ')') && (s.equals("0") || s.equals("") || ch1[length1]=='.')) {
}
else {
numberText.setText("");
expressText.setText(expressText.getText() + s + "*");
}
}
//当按钮为除号时
if(e.getSource().equals(buttonexcept)) {
s=numberText.getText();
char ch1[] = s.toCharArray();
int length1 = s.length() - 1;
String S = expressText.getText();
char ch2[] = S.toCharArray();
int length2 = S.length() - 1;
if((length2 == -1 ||ch2[length2] != ')') && (s.equals("0") || s.equals("") || ch1[length1]=='.')) {
}
else {
numberText.setText("");
expressText.setText(expressText.getText() + s + "/");
}
}
//当按钮为左括号时
if(e.getSource().equals(buttonleft)) {
if(!numberText.getText().equals("0") && !numberText.getText().equals("")) { //判断输入括号的前一个数字非空非零;
expressText.setText(expressText.getText() + numberText.getText()); //如果先向表达式中更新输入的数据
}
s = expressText.getText();
char ch[] = s.toCharArray();
int length = s.length() - 1;
if(length == -1 || ch[length] == '+' || ch[length] == '-' || ch[length] == '*' || ch[length] == '/' || ch[length] == '(' ) {
//括号左边是否有数或符号类别的判断;
expressText.setText(expressText.getText() + '('); //满足条件则加入左括号;
leftNum++; //左括号数加一标记;
}
}
//当按钮为右括号时;
if(e.getSource().equals(buttonright)) {
if(!numberText.getText().equals("0")) {
expressText.setText(expressText.getText() + numberText.getText());
numberText.setText(""); //与左括号不同,右括号输入后需要对数字框置空因为左括号前执行了符号操作数字框时空的;
}
s=expressText.getText();
char ch[] = s.toCharArray();
int length = s.length() - 1;
if(Character.isDigit(ch[length]) && leftNum > rightNum) { //只有前面是数字的时候且左括号的数量大于右括号的数量的时候才能加右括号;
rightNum++; //右括号数加一标记;
expressText.setText(expressText.getText() + ')');
}
}
//当按下C键时;
if(e.getSource().equals(buttonC)) {
numberText.setText("0"); //置当前数字框为0;
expressText.setText(""); //置空当前表达式框;
leftNum = 0; //当一次计算完成之后,只有按CE按钮才能进行新的计算,因为要清除所有标志位否则会影响下一次操作;
rightNum = 0;
pointbook = 0;
equalbook = 0;
}
//当按钮为CE时,
if(e.getSource().equals(buttonCE)) {
numberText.setText("0"); //清除当前数字框中内容;
pointbook = 0; //更新小数点状态为0;
}
//如果按钮为H时,输出历史结果;
// if(e.getSource().equals(buttonhistoricalrecords)) {
//numberText.setText("0"); //从历史数组中提取历史保存上一个的结果;
// int hr = Integer.parseInt( numberText.getText());
// String hr2 = "第"+hr+"个历史结果为:";
// expressText.setText( String.valueOf(history.peek()));
//}
//当按钮为1/x时把输入框中的值变为倒数
if(e.getSource().equals(buttoninvert) ) {
s = numberText.getText();
//等于0的时候不进行操作
if(s.equals("0")) {
}
else {
double a = Double.parseDouble(numberText.getText()); //字符转换为double类型的数值;
a = 1/a;
numberText.setText(String.valueOf(a)); //再把数值转换为string类型的字符;
}
}
//当按钮为等于号时 (核心算法部分)
if(e.getSource().equals(buttonequal)) {
s=numberText.getText();
if(!s.equals("0") && !s.equals("")) {
expressText.setText(expressText.getText() + s); //当前已有表达式非0非空输入等号前的操作数;
}
if(equalbook == 0) { //当等号标志位为0时才能输入等号;
numberText.setText("");
for(int i = 0; i < leftNum - rightNum; i++) {
expressText.setText(expressText.getText() + ')'); //补全右括号
}
String[] ansString = new String[100]; //声明一个100空间大小的String类型的字符串保存结果;
int Size = 0;
Stack<Character> Operater = new Stack<Character>(); //声明一个栈
Stack<Double> history = new Stack<Double>(); //用以保存历史答案;
s = expressText.getText();
char ch[] = s.toCharArray();
int length = ch.length; //获取等号前的全部表达式;转换为字符数组;计算长度;
for(int j = 0; j < length; j++) {
//当数组元素为数字时
if(ch[j] >='0' && ch[j] <= '9') { //数字情况; //遍历表达式,遇到一个数字后开始执行以下操作;
double Number = ch[j] - '0'; //对字符数组中字符向double类型的强制转换;
int point = 0; //记录小数点是否出现
//记录小数点出现后的位数
int bit = 1;
if(j==length-1) {
ansString[Size++] = String.valueOf(Number); //如果表达式中最后一个数遍历完,则直接转换格式为答案并放入ansString中;
}
for(j++; j<length; j++){
if((ch[j] < '0' || ch[j] >'9') && ch[j]!='.') {
j--;
ansString[Size++] = String.valueOf(Number); //提取除一个完整操作数添加到ansString中;
break;
}
if(ch[j] == '.') {
point = 1;
continue; //小数点检测;
}
if(ch[j] >= '0' && ch[j] <= '9') { //如果为同一数字的不同位数执行以下操作;
if(point == 0) {
Number = Number * 10 + (ch[j] - '0');
} else {
Number = Number + Math.pow(10, -bit) * (ch[j]-'0'); //对同一个完整数字的读取与操作;
bit++; //操作数的位数计数;
}
}
}
}
else { //符号情况;
if(ch[j] =='(') { //对左括号压栈;
Operater.push('(');
}
else if(ch[j]==')') { //右括号情况;
while(!Operater.peek().equals('(')) { //检测栈中的栈顶是否为左括号;
ansString[Size++] = String.valueOf(Operater.peek()); //将栈中除左括号后的全部内容取出保存正在ansString中;
Operater.pop(); //将栈中除左括号外的全部弹出;
}
Operater.pop(); //弹出一个栈顶的左括号;
}
else { //运算符情况;
if(Operater.empty()) { //栈空情况
Operater.push(ch[j]); //如果栈空则压如符号
}
else { //栈非空情况;
while(true) {
if(Operater.empty()) {
Operater.push(ch[j]);
break;
}
char operater1 = Operater.peek(); //从栈中提取操作运算符;
if((ch[j] == '*' || ch[j] == '/') && (operater1=='+' || operater1=='-') || (operater1=='(' || operater1 == ')')){
Operater.push(ch[j]);
break;
}
else{
ansString[Size++] = String.valueOf(Operater.peek());
Operater.pop();
}
}
}
}
}
}
while(!Operater.empty()) { //栈非空对栈中数据全部存到ansString中再弹出栈;
ansString[Size++] = String.valueOf(Operater.peek());
Operater.pop();
}
//正式计算;
Stack<Double> Last = new Stack<Double>();
for(int i=0; i<Size; i++) { //目前ansSting中存有全部操作数和操作符;
String s1 = ansString[i];
char ch2[] = s1.toCharArray();
if(ch2[0]>='0' && ch2[0]<='9') {
Last.push(Double.parseDouble(s1)); //如果ch2为数则压栈;
}
else {
double num1 = Last.pop(); //弹出两个相邻操作数;
double num2 = Last.pop();
double num3 = 0;
if(ch2[0]=='+')
num3 = num2 + num1;
else if(ch2[0]=='-')
num3 = num2 - num1;
else if(ch2[0]=='*')
num3 = num2 * num1;
else if(ch2[0]=='/')
num3 = num2 / num1;
Last.push(num3); //结果入栈;
}
}
history.push(Last.peek());
expressText.setText(expressText.getText() + "=" + Last.pop());
equalbook = 1;
}
}
if(e.getSource().equals(buttonhistoricalrecords)) {
expressText.setText("");
expressText.setText( String.valueOf(history.peek()));
}
}
public static void main(String []args) {
Calculator calculator = new Calculator();
}
}