支持算术表达式求解的计算器——计算机软件实习
一、项目要求
1 .能通过设计的按钮控件输入并实现算术表达式,表达式在文本框中显示,运算结果输出显示;保存和浏览历史运算记录;
2 .能够检验算术表达式的合法性;
3 .能够实现混合运算的求解,算术表达式中包括加、减、乘、除、括号等运算符;
4 .要求交互界面友好,程序健壮。
二、项目分析
项目的具体实现需要一下两步
1 .搭建GUI界面
2 .利用双栈法实现算术表达式的求解
3 .使用语言:Java swing
三、已完成的功能与不足之处
3.1已完成的功能
1·.项目已完成包含(对)+,-,*,、,%的算术运算式求解
2.左右括号和其他的按钮功能基本添加完毕
3.2不足之处
1.文本域为空的时候直接点击左括号会在文本域添加双括号
2.负号的功能尚未添加
四、GUI界面的搭建
4.1 GUI的建立
下面给出单个控件的搭建方式,其他的按钮和文本域类似
1)首先是JFrame定义一个窗体对象
JFrame jf = new JFrame("计算器");
jf.setSize(600,500);//确定窗体大小
jf.setLocationRelativeTo(null);//设置窗体位置位于屏幕中央
jf.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);//设置窗体的关闭方式
2)确定布局方式,我使用的是绝对布局
JPanel panel = new JPanel(null);
3)文本域的建立,设置位置大小以及往面板中添加
JTextArea text = new JTextArea();
text.setLocation(20,10);//设置文本域的位置
text.setSize(350,50);//设置文本域的大小
panel.add(text);往面板里面文本域控件
4)按钮的建立,设置位置大小以及往面板中添加
JButton jb1 = new JButton("(");//按钮的建立
jb1.setBounds(20,130,50,50);//设置按钮的位置和大小
panel.add(jb1);
4.2 监听事件的重写
根据不同按钮的功能重写监听事件
1)数字按钮0-9监听事件的重写
以数字按钮1为例,它建立、设置位置大小、往面板中添加、重写监听事件
JButton jb6 = new JButton("1");
jb6.setBounds(20,190,50,50);
panel.add(jb6);
//数字按钮的监听事件是如果感受到了鼠标点击该按钮,就会往文本域里面添加该数字按钮的数字
jb6.addActionListener(new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
if(e.getActionCommand().equals("1")){
text.append("1");
}
}
});
2)运算符按钮(+,-,*,/,%)的监听事件
以运算符 * 为例
JButton jb24 = new JButton("*");
jb24.setBounds(245,370,50,50);
panel.add(jb24);
//如果感受到了鼠标点击该按钮,就会获取当前文本域的内容,对该内容进行判断,判断文本域的当前内容的最后一个是
否为数字或右括号,如果是的话就往文本域添加 * 运算符
jb24.addActionListener(new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
if(e.getActionCommand().equals("*")){
String str = text.getText();
if(!issame(str)){
text.append("*");
}
}
}
});
关于issame函数的介绍在最底下
3)小数点(.)的监听事件
JButton jb21 = new JButton(".");
jb21.setBounds(20,370,50,50);
panel.add(jb21);
jb21.addActionListener(new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals(".")) {
String str = text.getText();
char c = str.charAt(str.length() - 1);
if (c >= '0' && c <= '9') {
for (int i = str.length() - 1; i >= 0; i--) {
char c_1 = str.charAt(i);
boolean swr = (c_1 >= '0' && c_1 <= '9');
if (!swr) {
if (str.charAt(i) != '.') {
text.append(".");
break;
}
}
if (i == 0) {
text.append(".");
}
}
}
}
}
});
4)BACKSP, C的监听函数的内容
//BACKSP对当前文本域的最后一个字符进行删除
String str = text.getText();
text.setText("");
str = str.substring(0, str.length() - 1);
text.append(str);
//C清空文本域的内容
text.setText("");
五、双栈法的实现(即等号的监听主要内容)
5.1 链栈的建立
5.2 双栈法实现算术表达式求解
5.2.1 双栈法主要思想:
1)当扫描到的是运算数时,压入栈Opnd
2)当扫描到的是运算符时:
如这个运算符比OP栈顶运算符的优先级高,入栈
如这个运算符比OP栈顶运算符优先级低,则从OPnd栈中弹出两个运算数,从OP弹出栈顶元素进行运算,结果压入OPnd栈
3)继续处理当前字符,直到遇到结束符位置
运算符优先级比较
由上图可见,代码的实现关于运算符最好先处理左右括号,遇到左括号直接入OP栈,遇到右括号一直执行弹出运算压入的操作,直到栈顶元素是左括号结束,然后把左括号弹出OP栈。
关于操作数的处理由遇到第一个操作数操作是从这个操作数开始往后面遍历,直到遇到运算符和左右括号(若为第k个字符)为止,然后把该部分操作数合成一个double类型的数压入OPnd栈,然后把i赋值成k。(运算式的合法性我写在按钮的监听事件里面了,不合法就不能往文本域添加该按钮的内容。)
5.2.2 双栈法具体实现
部分函数将在最后显示
具体代码实现如下
String Arithmetic = text.getText();//获取文本域的算术表达式
Stack<Character> OP = new Stack<Character>();//建立OP栈(运算符栈)
Stack<Double> OPnd = new Stack<Double>();//建立OPnd(运算数)栈
OP.push('#');//给OP先压入优先级最低的#,防止栈顶为空
char[] Operation_characters = Arithmetic.toCharArray();//string转字符数组
for (int i =0; i < Arithmetic.length(); i++) {
if(Operation_characters[i] == '('){//左括号直接入栈
OP.push(Operation_characters[i]);
}
else {
if (issames(Operation_characters[i])) {//判断是否是数字或者小数点
String x_1 = "";
x_1 = x_1 + Operation_characters[i];
double y_1;
for (int j = i+1; j < Arithmetic.length(); j++) {
if(j ==Arithmetic.length()-1){
if (issames(Operation_characters[j])){
x_1 = x_1 + Operation_characters[j];
i=j;
}
else {
i = j-1;
break;
}
}else {
if (issames(Operation_characters[j])) {
x_1 = x_1 + Operation_characters[j];
}
else {
i = j-1;
break;
}
}
}
y_1 = Double.parseDouble(x_1);//把数double 数压入OPnd栈里面
OPnd.push(y_1);
}
else {
if(isop(Operation_characters[i])){//判断是否是运算符
char op = OP.peek();
boolean po = Compare_priorities(Operation_characters[i], op);//比较当前操作符和OP的栈顶元素的优先级大小
if (Operation_characters[i] == ')') {//先处理右括号
while (OP.peek() != '(') {
double s1 = OPnd.peek();
OPnd.pop();
double s2 = OPnd.peek();
OPnd.pop();
char c = OP.peek();
OP.pop();
//将结果压栈
OPnd.push(operation(s2, s1, c));
}
OP.pop();//把左括号弹出OP栈
}
else {
if (po) {
OP.push(Operation_characters[i]);//优先级高于栈顶入OP栈
}
else {
if(OP.peek() == '#'){
break;
}
else {
double s1 = OPnd.peek();
OPnd.pop();
double s2 = OPnd.peek();
OPnd.pop();
char c = OP.peek();
OP.pop();
//将结果压栈
OPnd.push(operation(s2, s1, c));
OP.push(Operation_characters[i]);
}
}
}
}
}
}
}
while (OP.peek() != '#') {
//取两个操作数和一个操作符计算
double s1 = OPnd.peek();
OPnd.pop();
double s2 = OPnd.peek();
OPnd.pop();
char c = OP.peek();
OP.pop();
//将结果压栈
OPnd.push(operation(s2, s1, c));
}
double result = OPnd.peek();
OPnd.pop();
text.append(String.valueOf(result));
5.2.3 部分自己写的函数
/**
* 判断str的最后一个是否为数字或右括号
* @param str 字符串
* @return 是返回false否则返回true
*/
public static boolean issame(String str){
char[] Operation_characters = str.toCharArray();
return Operation_characters[str.length() - 1] < '0' || Operation_characters[str.length() - 1] > '9' ;
}
/**
* 判断是否是数字和小数点
* @param str 字符串
* @return 是返回true,否则false
*/
public static boolean issames(char str){
return (str >= '0' && str <= '9') || str == '.';
}
/**
* 判断优先级
* @param x 运算符
* @return 返回int形,即运算符x的优先级
*/
public static int priorities(char x){
if(x == '+' || x== '-' ){
return 1;
} else if (x == '*' || x== '/' || x == '%') {
return 2;
} else if (x == '(') {
return 0;
} else if (x == ')') {
return 0;
} else if (x == '#') {
return -1;
}
return 4;
}
/**
*判断优先级大小,x大于y返回true
* @param x 运算符
* @param y 运算符
* @return x的优先级大于y返回true;否则false
*/
public static boolean Compare_priorities(char x,char y){
int a = priorities(x);
int b = priorities(y);
if(x=='(' && y=='('){
return false;
}
else {
return a > b || a == b;
}
}
/**
* 判断字符是否是运算符
* @param c 字符
* @return 是返回true,否则返回false
*/
public static boolean isop(char c)
{
return c == '+' || c == '-' || c == '*' || c == '/' || c == '(' || c == ')' || c == '#' || c == '%';
}
/**
* 求单个算式的结果
* @param str1 运算数1
* @param str2 运算数2
* @param x 运算符
* @return double类型的 = 运算数2 运算符 运算数1
*/
public static Double operation(double str1,double str2,char x){
double result = 0.00;
double a = str1;
double b = str2;
int c=0,d=0,e=0,f=0;//用来解决两个double数相乘出现很多小数点的问题
String str = String.valueOf(str1);
String str_1 = String.valueOf(str2);
for(int i=0;i<str.length();i++){
if(str.charAt(i) == '.'){
d = i;
break;
}
}
c= (int)(a*mi(d));
for(int i=0;i<str_1.length();i++){
if(str_1.charAt(i) == '.'){
f = i;
break;
}
}
e = (int)(b*mi(f));
if (x == '+') {
result = a + b;
} else if (x == '-') {
result = a - b ;
} else if (x == '*') {
result = (double) c * e /mi(d+f);
} else if (x == '/') {
result = a / b;
} else if (x == '%') {
result = a % b;
}
return result;
}
/**
* 求10的k次幂
* @param k 幂
* @return 10的k次幂
*/
public static int mi(int k){
int x = 1;
for(int i=0;i<k;i++){
x = x*10;
}
return x;
}