【项目一 支持算术表达式求解的计算器】

一、项目概述

基本要求:
1.能通过设计的按钮控件输入并实现算术表达式,表达式在文本框中显示且文本框不能编辑,运算结果输出显示;
2.能够检验算术表达式的合法性;
3.能够实现混合运算的求解,能正确识别算术表达式中包括加、减、乘、除、括号等运算符的优先级;
4.要求交互界面友好加粗样式,程序健壮。

实现语言:
java

二、项目分析

计算器软件设计的难点,在于中缀表达式转换为后缀表达式,以及计算后缀表达式的算法。以下是两种算法的思想:
1.中缀表达式转换为后缀表达式
(1)初始化两个栈:运算符栈s1和存储中间结果的栈s2
从左向有扫描中缀表达式
遇到操作数时将其压入s2
(2)遇到运算符时,比较其与s1栈顶运算符的优先级:
如果s1为空,或栈顶元素为’(’,则将此运算符入栈
否则,若优先级比栈顶运算符的优先级高,则将运算符压入s1栈
否则,将s1栈顶的运算符弹出并压入到s2中,再次转到1,与新的栈顶元素进行比较
(3)遇到括号时:
如果是’(’,则直接压入s1
如果是’)’,则依次弹出s1栈顶的运算符,并压入s2,知道遇到’('为止,此时将这一对括号丢弃
(4)重复以上步骤(2)和(3),直到表达式的最右边
(5)将s1中剩余的运算符依次弹出并压入s2,依次弹出s2中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式
2.计算后缀表达式
首先准备一个栈Res_Stack,用于存放计算的中间过程的值和最终结果
(1)从左开始向右遍历后缀表达式的元素
(2)如果取到的元素是操作数,直接入栈Res_Stack,如果是运算符,从栈中弹出2个数进行运算,然后把运算结果入栈
(3)当遍历完后缀表达式时,计算结果就保存在栈里了

三、软件代码

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Objects;
import javax.swing.*;

class SampleCalculator extends JFrame implements ActionListener {
    private final String[] KEYS = {
            "7", "8", "9", "C",
            "4", "5", "6", "+",
            "1", "2", "3", "-",
            "(", "0", ")", "*",
            "x^2", ".", "=", "/"
    };
    private JButton keys[] = new JButton[KEYS.length];
    private JTextArea resultText = new JTextArea(" ");
    private JTextArea History = new JTextArea();// 历史记录文本框初始值设为空
    private JPanel jp2=new JPanel();
    private JScrollPane gdt1=new JScrollPane(resultText);
    private JScrollPane gdt2=new JScrollPane(History);
    private JLabel label1 = new JLabel("Record");
    private JLabel label2 = new JLabel("Input");
    private String b = "";
    int flash=0;
    int flash2=0; //用来控制括号个数
    int flash3=0;
    int flash4=0; //数字后面不能加符号
    int flash5=1; //用来控制”)“后不能直接跟"("


    // 构造方法
    public SampleCalculator() {
        super("JavaCalculator");
        resultText.setBounds(20, 18, 255, 115);// 设置文本框大小
        resultText.setAlignmentX(RIGHT_ALIGNMENT);// 文本框内容右对齐
        resultText.setEditable(false);// 文本框不允许修改结果
        History.setBounds(290, 40, 250,270);// 设置文本框大小
        History.setAlignmentX(LEFT_ALIGNMENT);// 文本框内容右对齐
        History.setEditable(false);// 文本框不允许修改结果
        label1.setBounds(280, 2, 100, 20);//设置标签位置及大小
        label2.setBounds(20, 2, 100, 20);//设置标签位置及大小
        jp2.setBounds(280,20,180,280);//设置面板窗口位置及大小
        jp2.setLayout(new GridLayout());
        JPanel jp1 = new JPanel();
        jp1.setBounds(20,20,247,50);//设置面板窗口位置及大小
        jp1.setLayout(new GridLayout());
        resultText.setLineWrap(true);// 激活自动换行功能
        resultText.setWrapStyleWord(true);// 激活断行不断字功能
        resultText.setSelectedTextColor(Color.RED);
        History.setLineWrap(true);//自动换行
        History.setWrapStyleWord(true);
        History.setSelectedTextColor(Color.blue);
        gdt2.setViewportView(History);
        jp1.add(gdt1);
        jp2.add(gdt2);
        this.add(jp1);//将面板添加到总窗体中
        this.add(jp2);//将面板添加到总窗体中
        this.setLayout(null);
        this.add(label1);// 新建“Record”标签
        this.add(label2);// 新建“Input”标签

        // 放置按钮
        int x = 20, y = 79;
        for (int i = 0; i < KEYS.length; i++)
        {
            keys[i] = new JButton();
            keys[i].setText(KEYS[i]);
            if ((i>=0&&i<=2)){keys[i].setForeground(Color.blue);}
            if ((i>=4&&i<=6)){keys[i].setForeground(Color.blue);}
            if ((i>=8&&i<=10)){keys[i].setForeground(Color.blue);}
            if ((i==13)){keys[i].setForeground(Color.blue);}
            if(i==3||i==7||i==11||i==15||i==19)
            {
                keys[i].setBounds(x, y, 50, 40);
            }else {
                keys[i].setBounds(x, y, 60, 40);
            }
            if (x < 215){
                x += 65;
            } else {
                x = 20;
                y += 45;
            }
            if((i>=0&&i<=2)||(i>=4&&i<=6)||(i>=8&&i<=10)||(i>=12&&i<=14)) {
                keys[i].setBackground(Color.WHITE);
            }else{
                keys[i].setBackground(Color.GRAY);
            }
            this.add(keys[i]);
        }
        for (int i = 0; i < KEYS.length; i++)// 每个按钮都注册事件监听器
        {
            keys[i].addActionListener(this);
        }
        this.setResizable(false);
        this.setBounds(500, 200, 480, 345);
        this.setDefaultCloseOperation(EXIT_ON_CLOSE);
        this.setVisible(true);
    }

    // 事件处理
    public void actionPerformed(ActionEvent e)
    {
        //History.setText(b);//使输入的表达式显示在历史记录文本框中
        String label=e.getActionCommand();//获得事件源的标签
        if(Objects.equals(label, "="))//
        {
            resultText.setText(this.b);
            History.setText(History.getText()+resultText.getText());
            if(label.equals("="))//调用计算方法,得出最终结果
            {
                String[] s =houzhui(this.b);
                String result=Result(s);
                this.b=result+"";
                //更新文本框,当前结果在字符串b中,并未删除,下一次输入接着此结果以实现连续运算
                resultText.setText(this.b);
                History.setText(History.getText()+"="+resultText.getText()+"\n");
            }
        }
        else if(Objects.equals(label, "C"))//清空按钮,消除显示屏文本框前面所有的输入和结果
        {
            flash=0;
            flash2=0;
            flash4=0;
            flash5=1;
            this.b="";
            resultText.setText("0");//更新文本域的显示,显示初始值;
        }
        else if(Objects.equals(label, "x^2"))
        {
            String m=pfys(this.b);
            resultText.setText(this.b+"^2"+"="+m);//使运算表达式显示在输入界面
            History.setText(History.getText()+this.b+"^2"+"=");//获取输入界面的运算表达式并使其显示在历史记录文本框
            this.b=m;
        }
        else if(label.equals("+")&& flash4==1){
            if(b.charAt(b.length()-1)!='+'&&b.charAt(b.length()-1)!='-'&&b.charAt(b.length()-1)!='*'&&b.charAt(b.length()-1)!='/'&&b.charAt(b.length()-1)!='('&&b.charAt(b.length()-1)!='.'){
                this.b = this.b + label;
                resultText.setText(this.b);
                flash=1;
                flash4=0;
                flash5=1;
            }
        }
        else if(label.equals("-")&& flash4==1){
            if(b.charAt(b.length()-1)!='-'&&b.charAt(b.length()-1)!='+'&&b.charAt(b.length()-1)!='*'&&b.charAt(b.length()-1)!='/'&&b.charAt(b.length()-1)!='.') {
                this.b = this.b + label;
                resultText.setText(this.b);
                flash=1;
                flash4=0;
                flash5=1;
            }
        }else if(label.equals("*")&& flash4==1){
            if(b.charAt(b.length()-1)!='*'&&b.charAt(b.length()-1)!='-'&&b.charAt(b.length()-1)!='+'&&b.charAt(b.length()-1)!='/'&&b.charAt(b.length()-1)!='('&&b.charAt(b.length()-1)!='.') {
                this.b = this.b + label;
                resultText.setText(this.b);
                flash=1;
                flash4=0;
                flash5=1;
            }
        }else if(label.equals("/")&& flash4==1){
            if(b.charAt(b.length()-1)!='/'&&b.charAt(b.length()-1)!='-'&&b.charAt(b.length()-1)!='*'&&b.charAt(b.length()-1)!='+'&&b.charAt(b.length()-1)!='('&&b.charAt(b.length()-1)!='.') {
                this.b = this.b + label;
                resultText.setText(this.b);
                flash=1;
                flash4=0;
                flash5=1;
            }
        }
        else if(label.equals(".")){
            if (flash==0){
                this.b=this.b+label;
                resultText.setText(this.b);
                flash=1;
            }
        }
        else if(label.equals(")")&&flash2>0) {
            flash3++;
            flash2--;
            flash5=0;
            if (b.charAt(b.length()-1)!=' '&&b.charAt(b.length()-1)!='.'&&b.charAt(b.length()-1)!='-'&&b.charAt(b.length()-1)!='*'&&b.charAt(b.length()-1)!='/'&&b.charAt(b.length()-1)!='('&&b.charAt(b.length()-1)!='+') {
                this.b = this.b + label;
                resultText.setText(this.b);
            }
        }
        else if(label.equals("(")&&flash5==1) {
            flash2++;
            this.b = this.b + label;
            resultText.setText(this.b);
            System.out.println(flash2);
        }
        else if(!label.equals(")")&&!label.equals("/")&&!label.equals("*")&&!label.equals("+")&&!label.equals("-")&&!label.equals("("))
        {
            this.b=this.b+label;
            resultText.setText(this.b);
            flash4=1;
            // History.setText(History.getText()+this.b);
        }
        //History.setText(History.getText()+this.b);//使输入的表达式显示在历史记录文本框中
    }
    //将中缀表达式转换为后缀表达式
    private String[] houzhui(String str) {
        String s = "";// 用于承接多位数的字符串
        char[] opStack = new char[100];// 静态栈,对用户输入的操作符进行处理,用于存储运算符
        String[] postQueue = new String[100];// 后缀表达式字符串数组,为了将多位数存储为独立的字符串
        int top = -1, j = 0;// 静态指针top,控制变量j
        for (int i = 0; i < str.length(); i++)// 遍历中缀表达式
        // indexof函数,返回字串首次出现的位置;charAt函数返回index位置处的字符;
        {
            if ("0123456789.".indexOf(str.charAt(i)) >= 0) // 遇到数字字符的情况直接入队
            {
                s = "";// 作为承接字符,每次开始时都要清空
                for (; i < str.length() && "0123456789.".indexOf(str.charAt(i)) >= 0; i++) {
                    s = s + str.charAt(i);
                    //比如,中缀表达式:234+4*2,我们扫描这个字符串的时候,s的作用相当于用来存储长度为3个字符的操作数:234
                }
                i--;
                postQueue[j] = s;// 数字字符直接加入后缀表达式
                j++;
            }
            else if ("(".indexOf(str.charAt(i)) >= 0) {// 遇到左括号
                top++;
                opStack[top] = str.charAt(i);// 左括号入栈
            }
            else if (")".indexOf(str.charAt(i)) >= 0) {// 遇到右括号
                for (;;)// 栈顶元素循环出栈,直到遇到左括号为止
                {
                    if (opStack[top] != '(') {// 栈顶元素不是左括号
                        postQueue[j] = opStack[top] + "";// 栈顶元素出栈
                        j++;
                        top--;
                    } else { // 找到栈顶元素是左括号
                        top--;// 删除栈顶左括号
                        break;// 循环结束
                    }
                }
            }
            else if ("*%/+-".indexOf(str.charAt(i)) >= 0)// 遇到运算符
            {
                if (top == -1)
                {// 若栈为空则直接入栈
                    top++;
                    opStack[top] = str.charAt(i);
                }
                else if ("*%/".indexOf(opStack[top]) >= 0)
                {// 当栈顶元素为高优先级运算符时,让栈顶元素出栈进入后缀表达式后,当前运算符再入栈
                    postQueue[j] = opStack[top] + "";
                    j++;
                    opStack[top] = str.charAt(i);
                }
                else
                {
                    top++;
                    opStack[top] = str.charAt(i);// 当前元素入栈
                }
            }
        }
        while (top != -1) {// 遍历结束后将栈中剩余元素依次出栈进入后缀表达式
            postQueue[j] = opStack[top] + "";
            j++;
            top--;
        }
        return postQueue;
    }
    //平方运算方法
    public String pfys(String str) {
        String result = "";
        double a = Double.parseDouble(str), b = 0;
        b = Math.pow(a, 2);
        result = String.valueOf(b);
        return result;
    }

    // 计算后缀表达式,并返回最终结果
    public String Result(String[] str) {
        String[] Result = new String[100];// 顺序存储的栈,数据类型为字符串
        int Top = -1;// 静态指针Top
        for (int i = 0; str[i] != null; i++) {
            if ("+-*%/".indexOf(str[i]) < 0) {  //遇到数字,直接入栈
                Top++;
                Result[Top] = str[i];
            }
            if ("+-*%/".indexOf(str[i]) >= 0)// 遇到运算符字符,将栈顶两个元素出栈计算并将结果返回栈顶
            {
                BigDecimal x, y, n;
                x = BigDecimal.valueOf(Double.parseDouble(new BigDecimal(Result[Top]).toString()));// 顺序出栈两个数字字符串,并转换为double类型
                Top--;
                y = BigDecimal.valueOf(Double.parseDouble(new BigDecimal(Result[Top]).toString()));
                Top--;
                if ("*".indexOf(str[i]) >= 0) {
                    n = y.multiply(x);
                    Top++;
                    Result[Top] = String.valueOf(n);// 将运算结果重新入栈

                }
                if ("/".indexOf(str[i]) >= 0) {
                   /* if (x == 0)// 除数不允许为0
                    {
                        String s = "error!";
                        return s;
                    } else {*/
                    n = (y.divide(x,5, RoundingMode.HALF_UP));
                    Top++;
                    Result[Top] = String.valueOf(n);// 将运算结果重新入栈
                    //}
                }

                if ("-".indexOf(str[i]) >= 0) {
                    n = y.subtract(x);
                    Top++;
                    Result[Top] = String.valueOf(n);// 将运算结果重新入栈
                }
                if ("+".indexOf(str[i]) >= 0) {
                    n = y.add(x);
                    Top++;
                    Result[Top] = String.valueOf(n);// 将运算结果重新入栈
                }
            }
        }
        return Result[Top];// 返回最终结果
    }

    public static void main(String[] args) {
        SampleCalculator a = new SampleCalculator();
    }
}

四、运行界面

在这里插入图片描述

五、项目总结

本次计算器软件的设计让我接触到了Java awt和swing组件,使我熟悉了java图形用户界面的设计原理和程序结构。其次,对于中缀表达式转换为后缀表达式,以及计算后缀表达式的算法,我也有了更深入的理解,收获颇丰。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,这是一个计算器程序的基本框架,需要用到栈结构和中缀表达式转后缀表达式的算法。 首先,我们需要定义一个栈结构,可以使用数组或链表实现。栈的基本操作包括入栈、出栈、判断栈空和栈满等。 其次,需要实现中缀表达式转后缀表达式的算法。该算法的基本思路是从左到右遍历中缀表达式,遇到数字直接输出,遇到运算符则将其压入栈中,遇到括号则将其压入栈中,如果是右括号则将栈中的运算符依次输出直到遇到左括号为止。最后,将栈中的剩余运算符依次输出即可得到后缀表达式。 最后,根据后缀表达式进行计算。遍历后缀表达式,遇到数字则入栈,遇到运算符则从栈中取出相应的操作数进行运算,并将结果入栈。最后,栈中剩余的数字即为结果。 以下是一个简单的实现,仅供参考: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #define MAX_SIZE 100 typedef struct Stack { int data[MAX_SIZE]; int top; } Stack; void init_stack(Stack *s) { s->top = -1; } int is_empty(Stack *s) { return s->top == -1; } int is_full(Stack *s) { return s->top == MAX_SIZE - 1; } void push(Stack *s, int x) { if (is_full(s)) { printf("Stack is full.\n"); exit(EXIT_FAILURE); } s->data[++s->top] = x; } int pop(Stack *s) { if (is_empty(s)) { printf("Stack is empty.\n"); exit(EXIT_FAILURE); } return s->data[s->top--]; } int get_top(Stack *s) { if (is_empty(s)) { printf("Stack is empty.\n"); exit(EXIT_FAILURE); } return s->data[s->top]; } int is_operator(char c) { return c == '+' || c == '-' || c == '*' || c == '/'; } int priority(char c) { if (c == '+' || c == '-') { return 1; } else if (c == '*' || c == '/') { return 2; } else { return 0; } } void infix_to_postfix(char *infix, char *postfix) { Stack s; init_stack(&s); int i = 0, j = 0; while (infix[i] != '\0') { if (isdigit(infix[i])) { postfix[j++] = infix[i]; } else if (is_operator(infix[i])) { while (!is_empty(&s) && priority(get_top(&s)) >= priority(infix[i])) { postfix[j++] = pop(&s); } push(&s, infix[i]); } else if (infix[i] == '(') { push(&s, infix[i]); } else if (infix[i] == ')') { while (get_top(&s) != '(') { postfix[j++] = pop(&s); } pop(&s); } else { printf("Invalid expression.\n"); exit(EXIT_FAILURE); } i++; } while (!is_empty(&s)) { postfix[j++] = pop(&s); } postfix[j] = '\0'; } int postfix_eval(char *postfix) { Stack s; init_stack(&s); int i = 0; while (postfix[i] != '\0') { if (isdigit(postfix[i])) { push(&s, postfix[i] - '0'); } else if (is_operator(postfix[i])) { int op2 = pop(&s); int op1 = pop(&s); int result; switch (postfix[i]) { case '+': result = op1 + op2; break; case '-': result = op1 - op2; break; case '*': result = op1 * op2; break; case '/': result = op1 / op2; break; } push(&s, result); } else { printf("Invalid expression.\n"); exit(EXIT_FAILURE); } i++; } if (is_empty(&s)) { printf("Invalid expression.\n"); exit(EXIT_FAILURE); } int result = pop(&s); if (!is_empty(&s)) { printf("Invalid expression.\n"); exit(EXIT_FAILURE); } return result; } int main() { char infix[MAX_SIZE], postfix[MAX_SIZE]; printf("Enter an infix expression: "); fgets(infix, MAX_SIZE, stdin); infix[strlen(infix) - 1] = '\0'; // remove trailing '\n' infix_to_postfix(infix, postfix); printf("Postfix expression: %s\n", postfix); int result = postfix_eval(postfix); printf("Result: %d\n", result); return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值