实验一:简单计算器的实现代码

这个博客介绍了如何使用Java实现一个支持中缀表达式、高精度浮点数运算和错误处理的计算器。计算器引擎类包含中缀表达式转后缀表达式的方法,并通过栈进行后缀表达式的计算。此外,计算器类实现了按钮和键盘事件监听,允许用户输入和计算表达式。
摘要由CSDN通过智能技术生成
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");
    }
}

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值