本项目实现了一个通过网页访问的简单计算器,它可以对带括号的加减乘除表达式进行计算并将计算结果返回给用户,并且可以对用户输入的表达式进行合法性判断,以下是项目的界面展示:
使用者可以通过点击网页上的按钮来输入一个算数表达式,之后点击等于号便可以将结果展示在界面上,具体效果如下:
在这个计算器中主要使用了前端html,css,JavaScript,后端spring boot以及数据结构中栈的使用方式与相关的算法。
DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>计算器title>
<link rel="stylesheet" href="./css/style.css">
head>
<body>
<div class="container">
<div class="calculator dark">
<div class="display-screen">
<div id="display">div>
div>
<div class="buttons">
<table>
<tr>
<td><button class="btn-operator" id="clear">Cbutton>td>
<td><button class="btn-operator" id="/">÷button>td>
<td><button class="btn-operator" id="*">×button>td>
<td><button class="btn-operator" id="backspace"><button>td>
tr>
<tr>
<td><button class="btn-number" id="7">7button>td>
<td><button class="btn-number" id="8">8button>td>
<td><button class="btn-number" id="9">9button>td>
<td><button class="btn-operator" id="-">-button>td>
tr>
<tr>
<td><button class="btn-number" id="4">4button>td>
<td><button class="btn-number" id="5">5button>td>
<td><button class="btn-number" id="6">6button>td>
<td><button class="btn-operator" id="+">+button>td>
tr>
<tr>
<td><button class="btn-number" id="1">1button>td>
<td><button class="btn-number" id="2">2button>td>
<td><button class="btn-number" id="3">3button>td>
<td rowspan="2"><button class="btn-equal" id="equal" onclick="submit()">=button>td>
tr>
<tr>
<td><button class="btn-operator" id="(">(button>td>
<td><button class="btn-number" id="0">0button>td>
<td><button class="btn-operator" id=")">)button>td>
tr>
table>
div>
div>
div>
<script src="./js/script.js">script>
body>
html>
以上为html部分的代码,主要是对使用者的界面进行了整体布局,确定了各个按钮的位置与功能。
*{
margin: 0;
padding: 0;
box-sizing: border-box;
outline: 0;
transition: all 0.5s ease;
}
body{
font-family: sans-serif;
}
a{
text-decoration: none;
color: #fff;
}
body{
background-image: linear-gradient(to bottom right, rgb(10, 88, 232), rgb(41, 231, 225));
}
.container{
height: 100vh;
width: 100vw;
display: grid;
place-items: center;
}
.calculator{
position: relative;
height: auto;
width: auto;
padding: 20px;
border-radius: 10px;
box-shadow: 0 0 30px #000;
}
#display{
margin: 0 10px;
height: 150px;
width: auto;
max-width: 270px;
display: flex;
align-items: flex-end;
justify-content: flex-end;
font-size: 30px;
overflow-x: scroll;
}
#display::-webkit-scrollbar{
display: block;
height: 3px;
}
button{
height: 60px;
width: 60px;
border: 0;
border-radius: 30px;
margin: 5px;
font-size: 20px;
cursor: pointer;
transition: all 200ms ease;
}
button:hover{
transform: scale(1.1);
}
button#equal{
height: 130px;
}
.calculator{
background-color: #fff;
}
.calculator #display{
color: #0a1e23;
}
.calculator button#clear{
background-color: #ffd5d8;
color: #fc4552;
}
.calculator button.btn-number{
background-color: #c3eaff;
color: #000;
}
.calculator button.btn-operator{
background-color: #7ed0b0;
color: #f39408;
}
.calculator button.btn-equal{
background-color: #adf9e7;
color: #000;
}
以上是css部分的代码,主要对界面的颜色样式进行了美化。
const display = document.querySelector('#display');
const buttons = document.querySelectorAll('button');
const submit = function () {
let subdata = null;
// 定义表单对象
const data = {}
// 获取input框内内容
const displayData = display.innerText
data.display = displayData
console.log(data);
const req = fetch('http://localhost:8081/cal/c', {
body: JSON.stringify(data),
method: "POST",
headers: {
'Content-Type': 'application/json'
}
})
req.then(res => res.text())
.then(res => {
subdata = JSON.parse(res);
if (!subdata.status)
display.innerText = '输入格式错误'
else {
console.log(subdata);
display.innerText = subdata.result
}
})
.catch(err => {
console.log(err)
})
}
buttons.forEach((item) => {
item.onclick = () => {
if (item.id == 'clear') {
display.innerText = '';
} else if (item.id == 'backspace') {
let string = display.innerText.toString();
display.innerText = string.substr(0, string.length - 1);
} else if (display.innerText != '' && item.id == 'equal') {
submit()
} else if (display.innerText == '' && item.id == 'equal') {
display.innerText = 'Empty!';
setTimeout(() => (display.innerText = ''), 2000);
} else {
display.innerText += item.id;
}
}
})
const calculator = document.querySelector('.calculator');
以上是JavaScript部分的代码,主要负责按钮的点击事件、给后端发送请求以及对后端返回结果的处理。
@RestController
@RequestMapping("/cal")
public class Controller {
@Autowired
private ServiceImpl service;
@PostMapping("/c")
public Res calcula(@RequestBody data data) {
return service.calculate(data.getDisplay());
}
}
@Service
public class ServiceImpl {
public Res calculate(String text) {
return new Res(Calculator.isValidExpression(text) ?
Calculator.calculateExpression(text) : null,
Calculator.isValidExpression(text) ? 1 : 0);
}
}
以上是后端给出的请求接口以及业务逻辑层的方法。
@Data
@AllArgsConstructor
public class Res {
public Integer result;
public int status;
}
public class data {
private String display;
public data() {
}
public String getDisplay() {
return display;
}
public void setDisplay(String display) {
this.display = display;
}
public data(String display) {
this.display = display;
}
@Override
public String toString() {
return "data{" +
"display='" + display + '\'' +
'}';
}
}
以上是给前端返回结果的包装类以及接收前端数据的实体类。
public class Calculator {
public static int priority(int oper) {
if (oper == '*' || oper == '/') {
return 1;
} else if (oper == '+' || oper == '-') {
return 0;
} else {
return -1;
}
}
public static boolean isOper(char val) {
return val == '+' || val == '-' || val == '*' || val == '/';
}
public static int cal(int num1, int num2, int oper) {
int res = 0;
switch (oper) {
case '+':
res = num1 + num2;
break;
case '-':
res = num2 - num1;
break;
case '*':
res = num1 * num2;
break;
case '/':
res = num2 / num1;
break;
}
return res;
}
public static int calculateExpression(String expression) {
Stack<Integer> numStack = new Stack<>();
Stack<Character> operStack = new Stack<>();
int index = 0;
int num1 = 0;
int num2 = 0;
int oper = 0;
int res = 0;
char ch = ' ';
String keepNum = "";
while (true) {
ch = expression.substring(index, index + 1).charAt(0);
if (isOper(ch)) {
if (!operStack.isEmpty()) {
if (priority(ch) <= priority(operStack.peek())) {
num1 = numStack.pop();
num2 = numStack.pop();
oper = operStack.pop();
res = cal(num1, num2, oper);
numStack.push(res);
operStack.push(ch);
} else {
operStack.push(ch);
}
} else {
operStack.push(ch);
}
} else if (ch == '(') {
int endIndex = getEndBracketIndex(expression, index);
String subExpression = expression.substring(index + 1, endIndex);
int subRes = calculateExpression(subExpression);
numStack.push(subRes);
index = endIndex;
} else {
keepNum += ch;
if (index == expression.length() - 1) {
numStack.push(Integer.parseInt(keepNum));
keepNum = "";
} else {
if (isOper(expression.substring(index + 1, index + 2).charAt(0))) {
numStack.push(Integer.parseInt(keepNum));
keepNum = "";
}
}
}
index++;
if (index >= expression.length()) {
break;
}
}
while (!operStack.isEmpty()) {
num1 = numStack.pop();
num2 = numStack.pop();
oper = operStack.pop();
res = cal(num1, num2, oper);
numStack.push(res);
}
return numStack.pop();
}
private static int getEndBracketIndex(String expression, int startIndex) {
Stack<Integer> stack = new Stack<>();
for (int i = startIndex; i < expression.length(); i++) {
char ch = expression.charAt(i);
if (ch == '(') {
stack.push(i);
} else if (ch == ')') {
stack.pop();
if (stack.isEmpty()) {
return i;
}
}
}
return 0;
}
public static boolean isValidExpression(String expr) {
// 去除空格
expr = expr.replaceAll("\\s", "");
// 使用栈保存左括号
Stack<Character> stack = new Stack<>();
for (int i = 0; i < expr.length(); i++) {
char c = expr.charAt(i);
if (isLeftParenthesis(c)) {
stack.push(c);
} else if (isRightParenthesis(c)) {
if (stack.isEmpty()) {
return false;
} else {
stack.pop();
}
} else if (isOperator(c)) {
if (i == 0 || i == expr.length() - 1 || isOperator(expr.charAt(i - 1)) || isOperator(expr.charAt(i + 1))) {
return false;
}
if (c == '/' && (i == expr.length() - 2 || expr.charAt(i + 2) == '0')) {
return false;
}
} else if (!Character.isDigit(c)) {
return false;
}
}
return stack.isEmpty();
}
public static boolean isLeftParenthesis(char c) {
return c == '(';
}
public static boolean isRightParenthesis(char c) {
return c == ')';
}
public static boolean isOperator(char c) {
return c == '+' || c == '-' || c == '*' || c == '/';
}
}
以上是对表达式进行处理并计算结果的一个简单的计算器类,该类提供了以下几个方法: