简单的计算器代码
package p1;
/*
0,使用步骤3步:
1,打开cmd 使用cd命令进入该文件所在的目录(切换盘符命令如“D:”)
2,编译命令javac MyCalc.java -d ./
3,运行命令java p1.MyCalc
*/
import java.awt.*;
import java.awt.event.*;
import java.math.BigDecimal;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.Box;
/** *
* 有待优化的地方 BUG:
* 1,
* 2,增加括号运算()
* 3,末尾去0和小数点
* 4,文本框全选输入时 覆盖
* 5,界面优化
* 6,替换tf1模板
* 7,
* @author luther
* @version *1.0-2019/8.24
* *1.2-2019/8/25
* *增加了文本框输入时和Button输入时Esc按键清空功能,和Button输入时Backspace退格功能,
* *优化了程序对负数符号及算术运算符的识别
* */
public class MyCalc {
public static void main(String[] args) {
new Thread(() -> new MyCalc().init(), "线程1").start();
}
/**
* @author luther
* */
@FunctionalInterface
interface SimpleCalculation {
/**
* @param repre
* @return The result of new String
*/
String doCalc(String repre);
}
//多次需要使用的正则表达式,运算符,减号需要转义,否则表示范围了,如[a-z];运算数,包含整数或小数
private static final String OPERS = "(?<=\\d)[\\+\\-\\*/]";
private static final String NUMBER = "(\\-?\\d*\\.?\\d+)";//[(-?\\d*\\.\\d+)(-?\\d+)]
//定义是否需要清空操作,用于下一次输入前清空文本框,输入方式三种,1文本框输入2鼠标点击按钮3键盘输入
private boolean needCls = true;
//主窗口,使用默认的5方向管理器
private Frame f = new Frame("計算器");
//4*4按键区
private Panel p = new Panel(new GridLayout(5, 4, 2, 4));
//存放两个文本框,从上到下
private Box b = Box.createVerticalBox();
//输入框
private TextField tf = new TextField("括号功能暂不可用", 30);
private TextField tf1 = new TextField("Wellcome", 30);
//按键4*4按钮文本
private static final String[] NAMES = {
"(", ")", "cls", "del",
"7", "8", "9", "+", "4", "5", "6", "-",
"1", "2", "3", "*", "0", ".", "=", "/"};
//按键4*4按钮
private Button[] bs = new Button[NAMES.length];
//二元计算器
private static SimpleCalculation calc;
//初始化计算器,删除tf1的监听器
{
calc = new SimpleCalculation() {
@Override
public String doCalc(String repre) {
Matcher m = Pattern.compile(OPERS).matcher(repre);
m.find();
String oper = m.group();
m = Pattern.compile(NUMBER).matcher(repre);
m.find();
BigDecimal b1 = new BigDecimal(m.group());
m = Pattern.compile("(?<=" + OPERS + ")" + NUMBER).matcher(repre);
m.find();
BigDecimal b2 = new BigDecimal(m.group());
switch (oper) {
case "+" :
return b1.add(b2).doubleValue()+"";
case "-" :
return b1.subtract(b2).doubleValue()+"";
case "*" :
return b1.multiply(b2).doubleValue()+"";
case "/" :
return b1.divide(b2, 20, BigDecimal.ROUND_HALF_UP).doubleValue()+"";
default :
return "未知错误2!";
}
}
};
//删除tf1所有监听器,好像没什么用,后期可以考虑用别的模板替换tf1
ActionListener[] as = tf1.getActionListeners();
for(ActionListener a : as) {
tf1.removeActionListener(a);
}
}
/**
* 初始化入口
*/
public void init() {
/*输入框背景色,石英色 #D9D9F3 李子色 #EAADEA
颜色网址:https://zhidao.baidu.com/question/582597145570474685.html
*/
tf1.setBackground(new Color(0x9F5F9F));
tf.setBackground(new Color(0xD9D9F3));
b.setBackground(new Color(0xFF7F00));
p.setBackground(new Color(0xD9D9F0));
//输入框注册按键监听器,功能1,DELETE清空,2,计算后,清空文本区;3,Enter回车键进行计算
tf.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
needCls();
if(e.getKeyCode() == KeyEvent.VK_DELETE) {
tf.setText("");
}
else if(e.getKeyCode() == KeyEvent.VK_ENTER){
doCalc();
//needCls = false;
}
}
});
//4*4按钮监听器
ActionListener bListener = new ActionListener() {
public void actionPerformed(ActionEvent e) {
needCls();
String cmd = e.getActionCommand();
if(cmd.equals("=")) {
doCalc();
}
else if(cmd.equals("del")) {
String s = tf.getText();
tf.setText(s.length() == 0 ? "" : s.substring(0, s.length() - 1));
}
else if(cmd.equals("cls")) {
tf.setText("");
}
else{
tf.setText(tf.getText() + cmd);
}
}
};
//4*4按键监听器
KeyAdapter bkListener = new KeyAdapter() {
public void keyPressed(KeyEvent e) {
String code = KeyEvent.getKeyText(e.getKeyCode());
needCls();
if(code.equals("Enter")) {
doCalc();
}
else if(code.equals("Backspace")) {
String s = tf.getText();
tf.setText(s.length() == 0 ? "" : s.substring(0, s.length() - 1));
}
else if(code.equals("Delete")) {
tf.setText("");
}
else if(code.startsWith("NumPad") || code.matches("\\d")) {
code = code.substring(code.length() - 1);
tf.setText(tf.getText() + code);
}
else{
//
}
}
};
//初始化按键,注册监听器ActionListener和KeyListener,并组合成4*4按键区
for(int i = 0; i < NAMES.length; i++) {
bs[i] = new Button(NAMES[i]);
bs[i].addActionListener(bListener);
bs[i].addKeyListener(bkListener);
bs[i].setBackground(new Color(0xD9D9F3));
//bs[i].setBounds(0, 0, 1, 2);
p.add(bs[i]);
}
//Frame注册WindowListener监听器,实现关闭按钮
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
Runtime.getRuntime().exit(0);
}
});
//Frame组合,初始化
b.add(tf1);
b.add(tf);
f.add(b, BorderLayout.NORTH);
f.add(p);
f.setLocation(800, 300);
f.pack();
f.setVisible(true);
}
//在执行输入之前,调用此方法执行清空操作,如果需要的话
private void needCls() {
if(needCls) {
tf.setText("");
needCls = false;
}
}
/**
* 执行计算
* 获取计算表达式,显示计算结果
*/
private void doCalc() {
tf1.setText(tf.getText() + "=");
tf.setText(recCalc(tf.getText()));
}
/**
* 递归计算recursive algorithm
* @param repre 需要计算的字符串
* @return 计算的结果
*/
private String recCalc(String exp) {
//去掉小数点和0
//if(exp.endsWith("0"))
//表达式校验1,只有一个数的表达式,2只需一次计算的表达式,3多次运算的表达式,4无法计算的表达式,
if(exp.matches(NUMBER)) {
return exp;
}
else if(exp.matches(NUMBER + OPERS + NUMBER)) {
//两种情况进行了计算,只需在这里需要清空,下面的递归也会执行到这里
needCls = true;
return calc.doCalc(exp);
//return simpleCalc(exp);
}
else if(exp.matches("^" + NUMBER + "(" + OPERS + NUMBER + ")+$")) {
//两个字符串:子计算结果和新表达式,进入该分支必定是包含2个运算符及以上
String subResult = "";
String result = exp;
//获取子表达式,用正则表达式
Matcher m = Pattern.compile(NUMBER + "[\\*/]" + NUMBER).matcher(exp);
if (m.find()) {
subResult = m.group();
}
else {
m = Pattern.compile(NUMBER + OPERS + NUMBER).matcher(exp);
m.find();
subResult = m.group();
}
//替换子表达式转换成正则表达式即子字符串的+和*需要转义
String regex = subResult.replaceFirst("\\b(?=[\\+\\*])", "\\\\");//"\\b+(?=[\\+\\*])", "\\\\"
//把子表达式替换成计算结果
result = result.replaceFirst(regex, calc.doCalc(subResult));//simpleCalc(subResult)
return recCalc(result);
}
else {
needCls = true;
return "无法计算";
}
}
}