模拟Windows系统自带的标准计算器
完成功能:
多位数(小数默认最多保留4位小数)加减乘除、括号、取反、退格、CE清空数字、C重置——键盘输入或鼠标点击都可
开方、求倒未实现(扩展很容易),M系按键未实现(不知道干嘛用的)
若还存在输入bug,请评论指出=.=,我来修改
运行效果:
实现方式:
计算器核心:
1、表达式存储->Vector<String>:使用一个动态的字符串数组来存储中缀表达式,为什么不用字符串呢,如果你以前写过计算器或者看过别人写的代码,大多数人写的要么只支持一位数的运算,要么存在很多bug,为什么有这些问题,因为把表达式当成一个字符串的话,数与数之间,数与运算符之间,以及数的检测是个让人很头疼的问题,所以这里才用字符串数组,比如你的表达式是(15+2)*3,那么这个字符串数组应该是{“(”,“15”,“+”,“2”,“)”,“*”,“3”},这样就避免了很多问题
2、表达式转换(中缀->后缀)->trans()函数:使用库中的栈(Stack,Java和C++都有)来进行转换,为什么要转换成后缀表达式呢?是因为电脑并不能识别运算的优先顺序(比如先乘除后加减)、而后缀表达式就是通过比较优先级将得到的,所以计算机如果拿到了后缀表达式,就可以之间运算。
3、表达式运算->counter()函数:得到了表达式之后就很容易了,逐个出栈进行运算就行了,但要注意的是除法运算除数不为0;
图形界面:
使用javaFx做的界面,细节没什么好说的。
C++:https://blog.csdn.net/qq_40946921/article/details/83353033
无图形界面,只支持一位数运算,可扩展,因为java的计算器核心代码其实就是从C++这搬过去的,可以看一下java代码的数据类型和counter,trans函数,很容易就能得到C++版本的代码。
Java:
import javafx.application.Application;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.FontPosture;
import javafx.scene.text.FontWeight;
import javafx.stage.Stage;
import javax.xml.soap.Text;
import java.lang.reflect.Array;
import java.text.DecimalFormat;
import java.util.*;
public class Calculate extends Application{
Vector<String> m_exp=new Vector<>(),p_exp=new Vector<>(); //m_exp[]为中缀表达式字符串数组,p_exp[]为后缀
GridPane pane =new GridPane(); //网格布局面板
Pane box=new Pane(); //显示面板
double result=0; //运算结果
//number表示当前数,exp为表达式
Label number=new Label("0"),exp=new Label();
//按钮
Button num[]=new Button[11],operator[]=new Button[4],other[]=new Button[13];
//按钮text
String op[]={"+","-","*","/"},ot[]={"MC","MR","MS","M+","M-","←","CE","C","±","√","%","1/x","="},out;
boolean shift=false,lastIsNumber=false,havePut=true,couldBack=false;
//面板
public void loadPane(){
//创建10个数字键及小数点按钮并绑定事件
for(int i=0;i<11;i++){
String tmp;
if(i<10)
tmp=String.valueOf(i);
else
tmp=".";
num[i]=new Button(tmp);
num[i].setMinSize(30,26);
num[i].setOnMouseClicked(e->putNum(tmp));
}
//创建4个运算符按键
for(int i=0;i<4;i++){
String tmp=op[i];
operator[i]=new Button(tmp);
operator[i].setMinSize(30,26);
operator[i].setOnMouseClicked(e->{
putExp(tmp);
});
}
for(int i=0;i<13;i++){
String tmp=ot[i];
other[i]=new Button(tmp);
other[i].setFont(Font.font(9));
other[i].setMinSize(30,26);
}
//other键布局
for(int i=2;i<4;i++)
for(int j=0;j<5;j++)
pane.add(other[(i-2)*5+j],j,i);
pane.add(other[10],4,4);
pane.add(other[11],4,5);
other[12].setMinSize(30,58);
pane.add(other[12],4,6,1,2);
//数字键布局
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
pane.add(num[(2-i)*3+j+1],j,i+4);
pane.add(num[0],0,7,2,1);
num[0].setMinSize(66,26);
pane.add(num[10],2,7);
//4个运算符键布局
for(int i=0;i<4;i++)
pane.add(operator[i],3,7-i);
pane.setPadding(new Insets(15,15,0,15));
pane.add(box,0,0,6,2);
pane.setHgap(6);
pane.setVgap(6);
pane.setBackground(new Background(new BackgroundFill(Color.rgb(217,228,242),null,null))); //设置面板背景色
exp.setMinWidth(170);
exp.setAlignment(Pos.BASELINE_RIGHT);
exp.setLayoutY(5);
number.setFont(Font.font("Arial", FontWeight.BOLD, FontPosture.REGULAR,10));
number.setMinWidth(170);
number.setMinHeight(30);
number.setFont(Font.font("微软雅黑", FontWeight.BOLD, FontPosture.REGULAR,20)); //设置显示数字格式
number.setLayoutY(20);
number.setAlignment(Pos.BASELINE_RIGHT);
Rectangle rectangle =new Rectangle(0,0,173,50);
rectangle.setFill(Color.WHITE);
rectangle.setStroke(Color.BLACK);
rectangle.setStrokeWidth(0.1);
box.getChildren().add(rectangle);
box.getChildren().add(number);
box.getChildren().add(exp);
}
//按键事件
public void loadKey(){
pane.setOnKeyPressed(e->{
if(e.getCode()== KeyCode.SHIFT)
shift=true;
if(shift){
if(e.getCode()==KeyCode.DIGIT9) {
m_exp.add("(");
exp.setText(exp.getText()+"(");
}
else if(e.getCode()==KeyCode.DIGIT0) {
if(havePut==false)
m_exp.add(number.getText());
m_exp.add(")");
lastIsNumber=false;
havePut=true;
exp.setText(getExp());
}
}
else{
switch(e.getCode()){
case DIGIT0:case NUMPAD0: putNum("0"); break;
case DIGIT1:case NUMPAD1: putNum("1"); break;
case DIGIT2:case NUMPAD2: putNum("2"); break;
case DIGIT3:case NUMPAD3: putNum("3"); break;
case DIGIT4:case NUMPAD4: putNum("4"); break;
case DIGIT5:case NUMPAD5: putNum("5"); break;
case DIGIT6:case NUMPAD6: putNum("6"); break;
case DIGIT7:case NUMPAD7: putNum("7"); break;
case DIGIT8:case NUMPAD8: putNum("8"); break;
case DIGIT9:case NUMPAD9: putNum("9"); break;
case BACK_SPACE:
if(number.getText().length()>1)
number.setText(number.getText().substring(0,number.getText().length()-1));
else{
number.setText("0");
lastIsNumber=true;
}
havePut=false;
break;
case ADD:putExp("+");break;
case SUBTRACT:putExp("-");break;
case MULTIPLY:putExp("*");break;
case DIVIDE:putExp("/"); break;
case EQUALS:case ENTER:
exp.setText("");
if(havePut==false)
m_exp.add(number.getText());
trans();
if(counter()==0)
number.setText(String.valueOf(result));
else
number.setText("除数不能为0");
havePut=true;
lastIsNumber=true;
m_exp.clear();
break;
}
}
});
pane.setOnKeyReleased(e->{if(e.getCode()== KeyCode.SHIFT)
shift=false;});
}
//事件
public void loadAction(){
other[5].setOnMouseClicked(e->{
if(couldBack) {
if (number.getText().length() > 1)
number.setText(number.getText().substring(0, number.getText().length() - 1));
else {
number.setText("0");
lastIsNumber = true;
}
havePut = false;
}
});
other[6].setOnMouseClicked(e->{number.setText("0");});
other[7].setOnMouseClicked(e->{m_exp.clear();result=0;havePut=false;lastIsNumber=false;number.setText("0");exp.setText("");});
other[8].setOnMouseClicked(e->{
if(number.getText().charAt(0)=='-')
number.setText(number.getText().substring(1,number.getText().length()));
else
number.setText("-"+number.getText());
havePut=false;
couldBack=true;
});
other[12].setOnMouseClicked(e->{
exp.setText("");
if(havePut==false)
m_exp.add(number.getText());
trans();
if(counter()==0)
number.setText(String.valueOf(result));
else {
number.setText("除数不能为0");
couldBack = false;
}
havePut=true;
lastIsNumber=true;
m_exp.clear();
});
}
//输入数字
public void putNum(String str){
if(havePut||number.getText().equals("0")||(havePut&&number.getText().length()>12))
if(str.equals("."))
number.setText(number.getText()+str);
else
number.setText(str);
else if(number.getText().length()<=12)
number.setText(number.getText()+str);
lastIsNumber=true;
havePut=false;
couldBack=true;
}
//输入符号
public void putExp(String str){
if(lastIsNumber){
m_exp.add(number.getText());
trans();
if(counter()==0) {
number.setText(String.valueOf(result));
}
else {
number.setText("除数不能为0");
couldBack=false;
}
m_exp.add(str);;
}
else if(m_exp.lastElement().equals("(")||m_exp.lastElement().equals(")")){
m_exp.add(str);
}
else {
m_exp.remove(m_exp.size() - 1);
m_exp.add(str);
}
lastIsNumber=false;
havePut=true;
exp.setText(getExp());
}
//退格
public void putBracket(String str){
if(lastIsNumber){
m_exp.add(str);
lastIsNumber=false;
havePut=true;
exp.setText(getExp());
}
}
//计算
int counter(){
double a,b;
Stack<Double> stack=new Stack<>();
for(int i=0;i<p_exp.size();i++){
if(p_exp.get(i).charAt(0)>='0'&&p_exp.get(i).charAt(0)<='9'||p_exp.get(i).length()>1){
stack.push(Double.valueOf(p_exp.get(i)));
}
else{
switch (p_exp.get(i)) {
case "+":
a = stack.pop();
b = stack.pop();
stack.push(a+b);
break;
case "*":
a = stack.pop();
b = stack.pop();
stack.push(a*b);
break;
case "-":
a = stack.pop();
b = stack.pop();
stack.push(b-a);
break;
case "/":
a = stack.pop();
b = stack.pop();
if(a==0)
return 1;
stack.push(b/a);
break;
}
}
}
if(stack.size()!=1)
return 1;
DecimalFormat df = new DecimalFormat("#.0000");
result=Double.valueOf(df.format(stack.peek()));
if(String.valueOf(result).length()>12){
result=(double)((int)(result/Math.pow(10,String.valueOf(result).length()-12))*Math.pow(10,String.valueOf(result).length()-12));
}
return 0;
}
Vector<String> trans(){
p_exp.clear();
Stack<String> stack=new Stack<>();
String operators=new String("+-*/(");
for (int i = 0; i < m_exp.size(); i++) {
if (operators.indexOf(m_exp.get(i).charAt(0)) != -1&&m_exp.get(i).length()==1) { //出现操作符
if (m_exp.get(i).charAt(0) == '(') //栈中添加左括号
stack.push(m_exp.get(i));
else { //操作符的优先级判断
while ((!stack.empty()) && (priority(m_exp.get(i).charAt(0)) <= priority(stack.peek().charAt(0)))) { //当栈不为空时,进行优先级判断
p_exp.add(stack.pop()); //若当前操作符优先级低于栈顶,弹出栈顶,放到后缀式中
}
stack.push(m_exp.get(i));
}
}
else if (m_exp.get(i).charAt(0) == ')') { //出现右括号时,将栈中元素一直弹出,直至弹出左括号
while (stack.peek().charAt(0) != '(') {
p_exp.add(stack.pop());
}
stack.pop(); //弹出左括号
}
else { //把操作数加入到后缀式中
p_exp.add(m_exp.get(i));
}
}
while (!stack.empty()) { //将栈中剩余操作符放到后缀式中
p_exp.add(stack.pop());
}
return p_exp;
}
//获取表达式
String getExp(){
out=new String();
for(int j =0;j<m_exp.size();j++){
out+=m_exp.get(j);
if(j!=m_exp.size()-1)
out+=" ";
}
if(out.length()>25)
out="<"+out.substring(out.length()-25);
return out;
}
//获取运算符优先级
int priority(char ch){
if (ch == '*'||ch=='/')
return 2;
if (ch == '+'||ch=='-')
return 1;
if (ch == '(')
return -1;
return 0;
}
public void start(Stage stage){
loadPane();
loadAction();
loadKey();
Scene scene=new Scene(pane,195,260);
stage.setScene(scene);
stage.setResizable(false);
stage.setTitle("计算器—Italink");
stage.show();
}
}