package zuizhong;
/**
* Java计算器
* 支持中缀表达式运算
* 支持高精度浮点数运算
* 支持按钮和键盘同步监听
* 支持错误处理并提示
*/
import java.awt.BorderLayout;//边界布局管理器
import java.awt.Color;//提供用于颜色空间的类
import java.awt.Container;//可以放置组件的容器,抽象窗口工具
import java.awt.GridLayout;//布局管理器,用于分割容器,各组件大小由所处区域决定
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.util.*;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.border.LineBorder;
/**
* 计算器引擎类(抽象类)
* 提供中缀表达式的计算功能
* 支持高精度浮点数
* 运算过程出现循环小数只处理至其小数点后8位
* @author wsw
* @throws Exception 计算过程中出现错误
*/
abstract class CalculatorEngine {
/**
* 运算符优先级判断
* @param op 运算符或者‘(’或者‘=’
* @return 优先级(最高4,最低0)
*/
static private int Precedence(char op) {
switch (op) {
case '+':
case '-':
return 1; // 定义加减运算的优先级为1
case '*':
case '/':
case '%':
return 2; // 定义乘除与取余运算的优先级为2
case '^':
return 3; // 定义次方运算的优先级为3
case '!':
return 4; // 定义阶乘运算的优先级为4
case '(':
case '=':
default:
return 0; // 定义在栈中的左括号和栈底字符的优先级为0
}
}
/**
* 中缀表达式转换后缀表达式
* @param str 中缀表达式字符串(要求以=结尾)
* @return 后缀表达式的字符串形式
* @throws Exception 中缀表达式存在错误
*/
static private String toSuffix(String str) throws Exception {
String d=str;
StringBuffer strBuf = new StringBuffer();// 存储后缀表达式
Stack<Character> stack = new Stack<Character>();// 字符栈,用于处理运算符
stack.push('=');// 压入栈底字符
int i = 0;// 中缀式字符串迭代器
if (str == null || str.equals("") || str.equals("="))
throw new Exception("表达式不能为空");
char ch = str.charAt(i);// 临时字符处理
if (ch == '.')
throw new Exception("表达式格式不规范(不能以“.”开头)");
if (str.charAt(str.length() - 1) != '=')
throw new Exception("表达式格式不规范(不以“=”结尾)");
if (ch == '-' || ch == '+')
strBuf.append("0 ");//正负号首位补零
while (ch != '=') {
if (ch == ' ')
ch = str.charAt(++i);// 读取下一个字符(忽略空格)
else if (ch == '(') {
stack.push('(');// 如果是左括号将其压入栈
ch = str.charAt(++i);
if (ch == '-' || ch == '+')
strBuf.append("0 ");//正负号补零
} else if (ch == ')') {//查看前面是否有左括号
while (stack.peek() != '(') {
if (stack.peek() == '=')//stack.peek是查看栈顶的元素但不移除它。pop方法会移除掉栈顶元素
throw new Exception("表达式括号不匹配,缺少“(”");
strBuf.append(stack.pop());//把栈顶元素追加到字符串strBuf后
}
stack.pop();
ch = str.charAt(++i);
} else if (ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '^' || ch == '!' || ch == '%') {
char w = stack.peek();// 临时读取栈中的运算符
// 将现在的运算符和栈中的运算符进行优先级比较
while (Precedence(w) >= Precedence(ch)) {
strBuf.append(w);//把运算符w追加到字符串strBuf后
stack.pop();//弹出该运算符,进行下一个运算符优先级的比较
w = stack.peek();
}
stack.push(ch);//将运算符ch压入栈
ch = str.charAt(++i);
} else {
// 直接将数字和小数点加到后缀式中,把小数点的优先级设定成和数字一样
if (!((ch >= '0' && ch <= '9') || ch == '.'))
throw new Exception("表达式出现非法符号");
while ((ch >= '0' && ch <= '9') || ch == '.') {
strBuf.append(ch);//把数字ch追加到字符串strBuf后
ch = str.charAt(++i);
}
strBuf.append(' ');//把空格追加到字符串strBuf后
}
}
ch = stack.pop();
while (ch != '=') {//检查括号的完整性
if (ch == '(')
throw new Exception("表达式括号不匹配,缺少“)”");
else {
strBuf.append(ch);
ch = stack.pop();
}
}
strBuf.append('=');//把等于追加到字符串strBuf后
return strBuf.toString();//返回后缀表达式strBuf
}
/**
* 计算后缀表达式
* @param str 后缀表达式的字符串形式
* @return 高精度浮点数
* 如果计算过程中出现循环小数则取至小数点后15位
* @throws Exception 计算过程出现错误
*/
static private BigDecimal runSuffix(String str) throws Exception {
System.out.println(str);//输入后缀表达式str
Stack<BigDecimal> stack = new Stack<BigDecimal>();//进行大数计算的栈
int i = 0;//设置后缀表达式迭代器
BigDecimal num1, num2;//临时大数变量
StringBuffer strBuf;//
while (str.charAt(i) != '=') {
strBuf = new StringBuffer();
if (str.charAt(i) >= '0' && str.charAt(i) <= '9') {
while ((str.charAt(i) >= '0' && str.charAt(i) <= '9') || str.charAt(i) == '.')
strBuf.append(str.charAt(i++));//形成数字
try {//Java的异常处理机制
stack.push(new BigDecimal(strBuf.toString()));
} catch (Exception e) {
throw new Exception("表达式计算数出错");
}
} else if (str.charAt(i) == ' ')
i++;//忽略空格
else if (str.charAt(i) == '+') {
if (stack.size() < 2)
throw new Exception("表达式缺少计算数");
num1 = stack.pop();
num2 = stack.pop();
stack.push(num1.add(num2));//加法运算
i++;
} else if (str.charAt(i) == '-') {
if (stack.size() < 2)
throw new Exception("表达式缺少计算数");
num1 = stack.pop();
num2 = stack.pop();
stack.push(num2.subtract(num1));//减法运算
i++;
} else if (str.charAt(i) == '*') {
if (stack.size() < 2)
throw new Exception("表达式缺少计算数");
num1 = stack.pop();
num2 = stack.pop();
stack.push(num1.multiply(num2));//乘法运算
i++;
} else if (str.charAt(i) == '%') {
if (stack.size() < 2)
throw new Exception("表达式缺少计算数");
num1 = stack.pop();
num2 = stack.pop();
try {
BigInteger numT1 = num1.toBigIntegerExact();
BigInteger numT2 = num2.toBigIntegerExact();
stack.push(new BigDecimal(numT2.remainder(numT1).toString()));//取余运算
} catch (ArithmeticException e) {
throw new Exception("参与取余运算的数只能是整数,且除数不能为0");
} catch (Exception e) {
throw new Exception("表达式取余运算出现错误");
}
i++;
} else if (str.charAt(i) == '^') {
if (stack.size() < 2)
throw new Exception("表达式缺少计算数");
num1 = stack.pop();
num2 = stack.pop();
try {
BigInteger numT = num1.toBigIntegerExact();
if (numT.compareTo(new BigInteger("0")) == numT.compareTo(new BigInteger("999999999")))
throw new ArithmeticException();
stack.push(num2.pow(numT.intValue()));//次方运算
} catch (ArithmeticException e) {
throw new Exception("幂的范围只能是0到9999之间,且只能是整数");
} catch (Exception e) {
throw new Exception("幂运算出现错误");
}
i++;
} else if (str.charAt(i) == '!') {
if (stack.size() < 1)
throw new Exception("表达式缺少计算数");
num1 = stack.pop();
try {
BigInteger numT = num1.toBigIntegerExact();
if (numT.compareTo(new BigInteger("0")) == numT.compareTo(new BigInteger("9999")))
throw new ArithmeticException();
int count = numT.intValue();
num2 = new BigDecimal("1");
while (count > 0)
num2 = num2.multiply(new BigDecimal(count--));//阶乘运算
stack.push(num2);
} catch (ArithmeticException e) {
throw new Exception("阶乘的范围只能是0到9999之间,且只能是整数");
} catch (Exception e) {
throw new Exception("阶乘运算出现错误");
}
i++;
} else if (str.charAt(i) == '/') {
if (stack.size() < 2)
throw new Exception("表达式缺少计算数");
num1 = stack.pop();
num2 = stack.pop();
try {
stack.push(num2.divide(num1));//除法运算
} catch (ArithmeticException e) {
try {
//出现循环小数将其处理为小数点只保留8位
stack.push(num2.divide(num1, 8, RoundingMode.HALF_UP));
} catch (ArithmeticException e2) {
throw new Exception("除法运算出现错误(可能是0作了除数)");
}
}
i++;
}
}
if (stack.size() != 1)
throw new Exception("表达式缺少运算符");
return stack.pop();
}
/**
* 表达式计算
*
* @param str 普通表达式(中缀表达式)
* @return 高精度浮点数
* @throws Exception 计算过程出现错误
*/
static public BigDecimal run(String str) throws Exception {//调用上面的表达式转化函数
String strSuffix = toSuffix(str);
return runSuffix(strSuffix);
}
}
/**
* 计算器类
* 利用计算器引擎类实现
* 支持按钮和键盘的双方监听
* @author chishaxie
*/
class Calculator extends JFrame implements ActionListener, KeyListener {
private JPanel eastPanel = new JPanel();//右部面板,用于放置按钮
private JPanel westPanel = new JPanel();//左部面板,用于Use放置文本框和提示标签
private JTextArea showResult = new JTextArea(4, 28);//文本框
private JTextArea History = new JTextArea(8,28);// 历史记录文本框
private JScrollPane historyUser;
private JScrollPane showUser;//为文本框加上拖动条
private JButton[] button = new JButton[24];//24个按钮
private String str = "7 8 9 + 4 5 6 - 1 2 3 * 0 ( ) / . ^2 ^ % ! del clr =";//按钮的字符集
private StringBuffer strBuf;//显示与输入的字符串
private JLabel tips = new JLabel("这里是提示区域");//提示标签
private JLabel label = new JLabel("历史记录");
public Calculator() {
this.setTitle("Java简单计算器");//定义窗口标题
this.setSize(560, 300);//窗口大小
this.setResizable(false);//禁止用户调整大小
StringTokenizer strT = new StringTokenizer(str, " ");//将按钮字符集按空格分隔
Container c = this.getContentPane();//获取根容器
showResult.setBorder(new LineBorder(Color.BLACK, 1));//设置文本区域边框
showResult.setLineWrap(true);//文本区域可自动换行
showResult.setEditable(false);//文本区域不可编辑
showResult.addKeyListener(this);//文本区域增加键盘事件监听
showUser = new JScrollPane(showResult);//为文本区域加上拖动条
History.setBorder(new LineBorder(Color.BLACK, 1));//设置文本区域边框
History.setLineWrap(true);//文本区域可自动换行
History.setEditable(false);//文本区域不可编辑
History.addKeyListener(this);//文本区域增加键盘事件监听
historyUser = new JScrollPane(historyUser );//为文本区域加上拖动条
label.setBounds(30, 15, 100, 20);//设置标签位置及大小
this.add(label);// 新建“历史记录”标签
History.setLineWrap(true);//自动换行
History.setWrapStyleWord(true);
History.setSelectedTextColor(Color.blue);
westPanel.add(showUser, BorderLayout.CENTER);
westPanel.add(tips, BorderLayout.SOUTH);
westPanel.add(label, BorderLayout.NORTH);
westPanel.add(History,BorderLayout.NORTH);
eastPanel.setLayout(new GridLayout(6, 4, 2, 2));//布局
for (int i = 0; i < 24; i++)
eastPanel.add(button[i] = new JButton(strT.nextToken()));//创建按钮并加到右部面板中
for (int i = 0; i < 24; i++) {
button[i].addActionListener(this);//为按钮增加点击事件监听
button[i].addKeyListener(this);//增加键盘事件监听(防止聚焦在按钮上时文本区域无响应)
}
c.add(eastPanel, BorderLayout.EAST);
c.add(westPanel, BorderLayout.CENTER);
strBuf = new StringBuffer();
}
/**
* 计算处理 (通过计算器引擎类)
*/
private void run() {
List<String> list=new ArrayList<>();
// Iterator<String> it=list.iterator();
StringBuffer strBuff= strBuf;
strBuff.append('=');//表达式规范化以=结尾
try {
//计算并且显示
long beforeTime = System.currentTimeMillis();
String strTemp;
showResult.setText(strBuff.append(strTemp = (CalculatorEngine.run(strBuff.toString())).toString()).toString());
list.add(strBuff.toString());
int length = list.size();
int i=length;
StringBuffer d=null;
while(i--!=0)
History.setText(list.get(i));//在历史记录框里显示计算表达式及结果
//History.setText(strBuff.toString());
strBuf.setLength(0);//表达式字符串清空
strBuf.append(strTemp);//新的计算保存原来的结果
//History.setText(strBuf+strTemp);//在历史记录框里显示计算表达式及结果
tips.setText("计算已完成");
} catch (Exception ex) {
if (ex.getMessage() == null || ex.getMessage().equals(""))
tips.setText("发生未知错误");
else
tips.setText(ex.getMessage());//将错误输出到提示标签上
}
}
/**
* 点击事件处理
*/
public void actionPerformed(ActionEvent e) {
if (e.getSource() == button[23]) {
run();//点击=则计算
//History.setText(+'='+String.valueOf(strBuf));
} else if (e.getSource() == button[21]) {
strBuf.setLength(strBuf.length() > 0 ? strBuf.length() - 1 : 0);//删除一个字符,del
showResult.setText(strBuf.toString());//显示
} else if (e.getSource() == button[22]) {
strBuf.setLength(0);//清空,clr
showResult.setText(strBuf.toString());//显示
} else {
//计算表达式的形成
String s=strBuf.append(((JButton) e.getSource()).getText()).toString();
showResult.setText(s);
}
}
/**
* 键盘点击事件处理
*/
public void keyPressed(KeyEvent e) {
if ((e.getKeyChar() >= '0' && e.getKeyChar() <= '9') || e.getKeyChar() == '.' ||
e.getKeyChar() == '+' || e.getKeyChar() == '-' || e.getKeyChar() == '*' ||
e.getKeyChar() == '/' || e.getKeyChar() == '(' || e.getKeyChar() == ')' ||
e.getKeyChar() == '^' || e.getKeyChar() == '!' || e.getKeyChar() == '%' || e.getKeyChar() == ' ') {
//计算表达式的形成
showResult.setText(strBuf.append(e.getKeyChar()).toString());
} else if (e.getKeyCode() == KeyEvent.VK_ENTER || e.getKeyChar() == '=') {
run();//按下回车键或者=,则计算
} else if (e.getKeyCode() == KeyEvent.VK_BACK_SPACE) {
strBuf.setLength(strBuf.length() > 0 ? strBuf.length() - 1 : 0);//删除一个字符,退格键
showResult.setText(strBuf.toString());//显示
//History.setText(strBuf.toString());
}
}
public void keyReleased(KeyEvent e) {
}
public void keyTyped(KeyEvent e) {
}
}
class test {
public static void main(String[] args) {
Calculator cFrame = new Calculator();
cFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
cFrame.setVisible(true);
//JOptionPane.showMessageDialog(null, "请注意正确使用java表达式计算器!\n");
}
}
实验一:简单计算器的实现代码
最新推荐文章于 2022-07-31 14:39:25 发布
这个博客介绍了如何使用Java实现一个支持中缀表达式、高精度浮点数运算和错误处理的计算器。计算器引擎类包含中缀表达式转后缀表达式的方法,并通过栈进行后缀表达式的计算。此外,计算器类实现了按钮和键盘事件监听,允许用户输入和计算表达式。
摘要由CSDN通过智能技术生成