Java实现简单的计算器

181 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

前言

最近在复习着Java Swing的使用,在布局这块反复又看了很久,然后突然发现GirdLayout机器适合来做一个计算器的简单样子,所以花了一点时间做了一下,最后没想到是在处理结果的算法这一块花的时间最多,这个简单的计算器目前来说支持的是加减乘除和括号运算,当然之后可能会扩展三角函数等功能吧。。。。


下面我将从各个模块来展示对应的代码

一、主界面部分

这个部分就是一个展示计算器的界面,只是简单的套用了GirdLayout和几个button(所以最后写完发现好像Swing没有复习到多少hhh)

package com.UI;

import com.UI.Listener.buttonListener;

import java.awt.GridLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingConstants;

/*
 * 2015-06-10
 */

/***
 * 计算器的主页面的设置
 */
public class calcUI extends JFrame{

    JButton resultButton = new JButton("");
    JPanel[] panel = new JPanel[5];
    public calcUI()
    {
        setSize(500,400);
        //设置为GirdLayout
        setLayout(new GridLayout(6,1));
        add(resultButton);
        for(int i = 0;i < 5;i++){
            panel[i] = new JPanel();
            add(panel[i]);
            //gridLayout
            GridLayout gridLayout = new GridLayout(1,4,3,3);
            panel[i].setLayout(gridLayout);
        }
        //计算器输入
        String [] buttonNames = new String []{"<-","(",")","C","7", "8","9","/","4","5","6","*","1","2","3","-","0",".","=","+"};
        for (int i=0;i<buttonNames.length;i++) {
            JButton jButton = new JButton(buttonNames[i]);
            panel[i/4].add(jButton);
            // 添加监听器来监听计算器
            jButton.addActionListener(new buttonListener(resultButton));
        }
        //config resultButton
        resultButton.setSize(200, 50);
        resultButton.setHorizontalAlignment(SwingConstants.RIGHT);
        resultButton.setEnabled(false);
        setTitle("calcUI");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        calcUI demo = new calcUI();
        demo.setVisible(true);
    }

}

二、监听器部分

这部分使用的是按钮监听器,通过获取每个按钮对应的文字类型改变上方显示结果按钮给出的文字。

package com.UI.Listener;

import com.UI.Utils.Calc;

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class buttonListener implements ActionListener {
    JButton ansButton;
    String text;
    public void actionPerformed(ActionEvent e) {
        String actionCommand = e.getActionCommand();
        text = ansButton.getText();
        if ("=".equals(actionCommand)) {  // 如果选择的是求解则开始计算
            text = Calc.CalcAns(text);  // 将计算结果的作为工具类
        }
        else if(actionCommand.equals("C"))  // 清空
        {
            text = "";
        }
        else if(actionCommand.equals("<-"))  // 删除一位
        {
            if (text.length() <= 1) text = "";
            else text = text.substring(0,text.length()-1);
        }
        else
        {
            text += actionCommand;
        }
        ansButton.setText(text);
    }
    public buttonListener(JButton ansButton) {
        this.ansButton = ansButton;
    }
}


三、结果计算部分

这个部分的代码量甚至比前两部分加起来还多了,主要是要加上正确性判断,结果求解等等,虽然似乎jdk已经给出来了对应的函数,但是既然已经做了,这部分的代码还是自己写为好,求解的部分就是通过双栈,一个符号栈一个数字栈来进行处理,避免了各种if情况的判断。(虽然没必要,但是把它设置为了静态类)

package com.UI.Utils;

import java.math.BigDecimal;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;
import java.util.regex.Pattern;

public class Calc {
    /**
     * 以下部分为处理字符串包含的数字
     *
     * @param str 待处理的字符串
     * @return
     */
    public static String StringToNum(String str) {
        Pattern patternSc = Pattern.compile("[+-]*\\d+\\.?\\d*[Ee]*[+-]*\\d+");  // 判断是否为科学计数法
        Pattern patternNum = Pattern.compile("-?[0-9]+\\.?[0-9]*");  // 判断是否为数字
        if (patternSc.matcher(str).matches()) {
            BigDecimal bd = new BigDecimal(str);
            return bd.toPlainString();
        } else if (patternNum.matcher(str).matches())
            return str;
        else
            return "";
    }

    /***
     * 处理读入的字符串
     * @param s 读入的字符串
     * @return
     */
    public static Queue<String> ReadInNums(String s) {
        Queue<String> count = new LinkedList<String>();
        double sum = -1;
        double Point = -1;
        int flag = 1;
        for (int index = 0; index < s.length(); index++) {
            if (s.charAt(index) >= '0' && s.charAt(index) <= '9' || s.charAt(index) == '.') {  //为数字的情况
                if (index > 0 && s.charAt(index - 1) == '-')  //遇到减法变加法处理
                    flag = -1;
                if (s.charAt(index) == '.') {  // 判断是否为.
                    if (Point != -1 || sum == -1)  // 如果已经有或者只是单个的.则判断为错误
                        return null;
                    else
                        Point = 0;  // 标记为有小数点
                } else {
                    if (Point != -1)  //如果之前有小数点出现则说明现在是小数位
                        Point++;
                    if (sum == -1)
                        sum = s.charAt(index) - '0';
                    else
                        sum = sum * 10 + s.charAt(index) - '0';
                }
            } else {
                if (Point != -1)
                    sum = sum / Math.pow(10, Point);//如果当前数字有小数位则除去相应的小数位位数
                if (sum != -1)
                    count.offer(String.valueOf(flag * sum));
                if (s.charAt(index) == '-') {
                    if (index < s.length() - 1 && s.charAt(index + 1) >= '0' && s.charAt(index + 1) <= '9'
                            && index != 0 && s.charAt(index - 1) != '(')
                        count.offer(String.valueOf('+'));  // 减法做加法处理
                    else
                        count.offer(String.valueOf('-'));
                } else
                    count.offer(String.valueOf(s.charAt(index)));
                sum = -1;
                Point = -1;
                flag = 1;
            }
            if (index == s.length() - 1 && sum != -1) {//处理尾部为数字的情况
                if (Point != -1) sum = sum / Math.pow(10, Point);
                count.offer(String.valueOf(flag*sum));
            }
        }
        return count;
    }

    /***
     * 此函数用来判断字符串表达式是否正确
     * @param countString
     * @return
     */
    public static boolean StRight(String[] countString) {
        int countKuo = 0;  // 计算括号的数量
        int countChar = 0;  // 计算计算符的数量
        for (int index = 0; index < countString.length; index++) {
            String str = countString[index];
            System.out.println(str);
            if (str.equals("(")) {
                countKuo++;
            } else if (str.equals(")")) {
                countKuo--;
            } else if (StringToNum(str).length() != 0) {
                countChar--;
            } else
                countChar++;
            if (countKuo < 0) return false;
        }
        if (countKuo != 0 || countChar != -1) return false;
        return true;
    }

    /**
     * 处理方法总入口的函数
     *
     * @param s 待处理的字符串
     * @return
     */
    public static String CalcAns(String s) {
        Queue<String> count = ReadInNums(s);  // 处理读取的字符串
        if (count == null || count.size() == 0) return "error!";  // 如果存在错误则返回
        String[] countString = new String[count.size()];
        int index = 0;
        while (count.peek() != null) {
            countString[index++] = count.poll();
            System.out.println(countString[index - 1]);
        }
        boolean stRight = StRight(countString);  // 判断表达式是否正确
        if (stRight)
            return CalcNums(countString);
        return "error!";
    }

    /***
     * 计算数字结果
     * @param countString
     * @return
     */
    private static String CalcNums(String[] countString) {
        //  开两个栈,分别存操作符合操作数
        Stack<String> operator = new Stack<String>();  // 操作符
        Stack<Double> operatorNum = new Stack<Double>();  // 操作数
        for (String sIndex : countString) {
            String s = StringToNum(sIndex);
            if (s != null && s.length() != 0)  // 判断字符串为数字
                operatorNum.push(Double.parseDouble(s));
            else  // 字符串不为数字
                switch (sIndex.charAt(0)) {
                    case '+':
                    case '-':
                    case '*':
                    case '/':
                        while (operator.size() != 0 && (operator.peek() != null && operator.peek().equals('*')
                                || operator.peek().equals("/"))) {  // 前置的乘除需要先处理
                            if (operatorNum.size() < 2)
                            return "error";
                            else {
                                String opera = operator.pop();
                                double num2 = operatorNum.pop();
                                double num1 = operatorNum.pop();
                                if (opera.equals("/")) {
                                    if (num2 == 0) return "error!";
                                    else operatorNum.push(num1 / num2);
                                } else {
                                    operatorNum.push(num1 * num2);
                                }
                            }
                        }
                        operator.push(sIndex);
                        break;
                    case '(':
                        operator.push(sIndex);
                        break;
                    case ')':
                        while (!operator.peek().equals("(")) {  //遇到一个括号则需要清除对等的括号
                            if (unableToOperate(operator, operatorNum)) return "error";
                        }
                        operator.pop();
                        break;
                }
        }
        while(operator.size() != 0)
        {
            if (unableToOperate(operator, operatorNum)) return "error";
        }
        if(operatorNum.size() != 1)
            return "error";
        else {
            double nums = operatorNum.pop();
            if(nums == (int)nums) return String.valueOf((int)nums);
            return String.valueOf(nums);

        }
    }

    /***
     * 判断当前求和过程是否出错
     * @param operator
     * @param operatorNum
     * @return
     */
    private static boolean unableToOperate(Stack<String> operator, Stack<Double> operatorNum) {
        if (operatorNum.size() < 2)
            return true;
        else {
            String opera = operator.pop();
            double num2 = operatorNum.pop();
            double num1 = operatorNum.pop();
            if (opera.equals("/")) {
                if (num2 == 0) return true;
                else
                    operatorNum.push(num1 / num2);
            } else if (opera.equals("*"))
                operatorNum.push(num1 * num2);
            else if (opera.equals("+"))
                operatorNum.push(num1 + num2);
            else
                operatorNum.push(num1 - num2);
        }
        return false;
    }
}


总结

算是一个小练手?最近一直在学SSM导致没啥时间写代码了,不过想想其实SSM框架最终目的也只是为了更好地写代码,而不是喧宾夺主,没有它就不行,例如我这个代码好像根本没有用这个框架啥的,可能自己之前有点太过于看重这种形式了吧(当然也得学!)学完SSM后就回头去学JVM吧,把最基础的一些东西搞懂!