数据结构Demo——简单计算器
简单计算器
- 一、项目介绍
- 二、技术使用
- 三、具体代码实现
- 1.前端部分
- 2.后端部分
一、项目介绍
本项目实现了一个通过网页访问的简单计算器,它可以对带括号的加减乘除表达式进行计算并将计算结果返回给用户,并且可以对用户输入的表达式进行合法性判断,以下是项目的界面展示:
使用者可以通过点击网页上的按钮来输入一个算数表达式,之后点击等于号便可以将结果展示在界面上,具体效果如下:
二、技术使用
在这个计算器中主要使用了前端html,css,JavaScript,后端spring boot以及数据结构中栈的使用方式与相关的算法。
- 在前端中使用了html来对界面进行了整体的布局,然后使用了css来对界面效果做了美化,最后使用JavaScript来实现每个按钮的点击事件,并且通过Ajax将请求参数发给后台服务器,然后将后台处理的结果接收并处理,最后展示给用户。
- 在后端中主要使用了spring boot框架来搭建一个简单的服务器,并对前端发来的请求进行处理,最后将处理的结果返回给前端。
- 在处理表达式的时候主要使用了数据结构中有关栈的一些知识,通过对栈的使用来对表达式进行计算。
三、具体代码实现
1.前端部分
<!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">C</button></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">7</button></td><td><button class="btn-number" id="8">8</button></td><td><button class="btn-number" id="9">9</button></td><td><button class="btn-operator" id="-">-</button></td></tr><tr><td><button class="btn-number" id="4">4</button></td><td><button class="btn-number" id="5">5</button></td><td><button class="btn-number" id="6">6</button></td><td><button class="btn-operator" id="+">+</button></td></tr><tr><td><button class="btn-number" id="1">1</button></td><td><button class="btn-number" id="2">2</button></td><td><button class="btn-number" id="3">3</button></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">0</button></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.innerTextdata.display = displayDataconsole.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部分的代码,主要负责按钮的点击事件、给后端发送请求以及对后端返回结果的处理。
2.后端部分
@RestController
@RequestMapping("/cal")
public class Controller {@Autowiredprivate 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;}@Overridepublic 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 == '/';}
}
以上是对表达式进行处理并计算结果的一个简单的计算器类,该类提供了以下几个方法:
- priority(int oper):根据运算符的优先级,返回一个整数值,其中乘法和除法的优先级为1,加法和减法的优先级为0,其他情况返回-1。
- isOper(char val):判断给定字符是否为运算符(+、-、*、/)。
- cal(int num1, int num2, int oper):根据给定的两个数字和一个运算符,进行相应的计算并返回结果。运算符对应的计算包括加法、减法、乘法和除法。
- calculateExpression(String expression):通过指定的数学表达式进行计算,并返回计算结果。该方法使用两个栈来实现计算过程。一个栈用于存储数字,另一个栈用于存储运算符。遍历表达式的字符,根据字符的类型进行相应的操作。如果是运算符,则根据运算符的优先级决定是否进行计算;如果是左括号,则寻找对应的右括号,并将括号内的子表达式进行递归计算;如果是数字,则将数字压入数字栈中。最后,将剩余的运算符依次进行计算,直到栈为空,返回最终的计算结果。
- getEndBracketIndex(String expression, int startIndex):辅助方法,用于获取给定表达式中与指定左括号对应的右括号的位置。
- isValidExpression(String expr):判断给定的表达式是否为有效的数学表达式。方法首先去除字符串中的空格,然后使用栈来检查表达式中的括号是否匹配以及运算符的使用是否正确。具体规则如下:左括号入栈,遇到右括号出栈,如果栈为空则表示括号不匹配;如果遇到运算符,则判断其前后是否有运算符,以及除法运算符是否除以0,如果不符合规则则表达式无效;如果遇到非数字和非运算符的字符,则表达式无效。最后,如果栈为空,则表示括号匹配,返回true,否则返回false。
相关文章:

数据结构Demo——简单计算器
简单计算器 一、项目介绍二、技术使用三、具体代码实现1.前端部分2.后端部分 一、项目介绍 本项目实现了一个通过网页访问的简单计算器,它可以对带括号的加减乘除表达式进行计算并将计算结果返回给用户,并且可以对用户输入的表达式进行合法性判断&#…...
java实现多文件打包压缩,导出zip文件
一.实现多文件打包压缩 Testpublic void testZipFile() throws IOException {String filePath "D:\\导出压缩文件.zip";OutputStream outputStream new FileOutputStream(filePath);try (ZipOutputStream zipOutputStream new ZipOutputStream(outputStream)) {//…...
java-枚举类的使用
public enum MyEnum {ONE("一"),TWO("二"),THREE("三");private final String myNum;MyEnum(String myNum) {this.myNum myNum;}public String getMyEnum() {return myNum;} }调用 MyEnum num MyEnum.ONE; System.err.println(num.getMyEnum…...

Vue插槽
插槽的作用就是在组件中的指定位置传入指定的内容 比如我们有两个相同样式的分类栏,但是里面的内容不同,一个是展示图片,一个是展示ul列表: 这样的情况我们就可以使用插槽来实现。 一、默认插槽 (一)指定…...

学习c++的第二天
目录 数据类型 基本数据类型 typedef 声明 枚举类型 类型转换 变量类型 变量定义 变量声明 左值(Lvalues)和右值(Rvalues) 变量作用域 数据类型 基本数据类型 C 为程序员提供了种类丰富的内置数据类型和用户自定义的数…...

Android NDK开发详解之调试和性能分析的系统跟踪概览
Android NDK开发详解之调试和性能分析的系统跟踪概览 系统跟踪指南 “系统跟踪”就是记录短时间内的设备活动。系统跟踪会生成跟踪文件,该文件可用于生成系统报告。此报告有助于您了解如何最有效地提升应用或游戏的性能。 有关进行跟踪和性能分析的全面介绍&#x…...

AD9371 官方例程HDL JESD204B相关IP端口信号
AD9371 系列快速入口 AD9371ZCU102 移植到 ZCU106 : AD9371 官方例程构建及单音信号收发 ad9371_tx_jesd -->util_ad9371_xcvr接口映射: AD9371 官方例程之 tx_jesd 与 xcvr接口映射 AD9371 官方例程 时钟间的关系与生成 : AD9371 官方…...
蓝牙服务:优化体验,提高连接效率
文章目录 1. 对蓝牙连接进行优化2. 设备配对的缓存机制3. 优化蓝牙连接的稳定性 蓝牙技术已经成为我们生活中不可或缺的一部分,我们使用它进行音频传输、数据传输、设备连接等等。然而,有时蓝牙连接会让用户感到非常困扰,比如连接速度缓慢、连…...

SSM校园设备管信息管理系统开发mysql数据库web结构java编程计算机网页源码eclipse项目
选题理由 随着计算机网络及多媒体技术的广泛应用,互联网已成为高校办学的基础设施和必备条件,基于互联网的高校信息管理越来越综合化,越来越多的教学管理、行政管理工作将架构在互联网上,互联网正在变为学校实施教学、科研和管理…...

iOS的应用生命周期以及应用界面
在iOS的原生开发中,我们需要特别关注两个东西:AppDelegate和ViewController。我们主要的编码工作就是在AppDelegate和ViewControlle这两个类中进行的。它们的类图如下图所示: AppDelegate是应用程序委托对象,它继承了UIResponder类…...
Macos下安装使用Redis
Redis 是一个基于内存的key-value的结构数据库适合存储热点数据 Macos安装Redis https://redis.io/docs/getting-started/installation/install-redis-on-mac-os/安装redis brew install redis查看安装信息: brew info redis前台启动redis: redis-server后台启…...

Redis的四种部署方案
这篇文章介绍Reids最为常见的四种部署模式,其实Reids和数据库的集群模式差不多,可以分为 Redis单机模式部署、Redis主从模式部署、Redis哨兵模式部署、Cluster集群模式部署,其他的部署方式基本都是围绕以下几种方式在进行调整到适应的生产环境…...

Microsoft Edge不能工作了,可能原因不少,那么如何修复呢
Microsoft Edge打不开或不能加载网页是用户在Windows 10、Android、Mac和iOS设备上的网络浏览器上遇到的许多错误之一。其他Microsoft Edge问题可能包括浏览器窗口和选项卡冻结、网站崩溃、互联网连接错误消息以及丢失Microsoft Edge书签、收藏夹、密码和收藏。 Microsoft Edg…...

算法---缺失的第一个正数
题目 给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。 请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。示例 1:输入:nums [1,2,0] 输出:3 示例 2:输入:nums …...
【算法与数据结构】--算法应用--算法和数据结构的案例研究
一、项目管理中的算法应用 在项目管理中,算法和数据结构的应用涉及项目进度、资源分配、风险管理等方面。以下是一些案例研究,展示了算法在项目管理中的实际应用: 项目进度管理: 甘特图算法:甘特图是一种项目进度管理…...

java如何获取调用接口的ip?
获取调用者的ip 场景:想知道哪个ip访问的某个接口时,就需要打印出来看看,这时就可以使用这个方法了。 案例: //HttpServletRequest 入参加上,请求对象public ForkResponse queryXXX(RequestBody XXXX xxxx, HttpServletRequest …...
ubuntu 18 更新git版本到 2.80.1
前言 使用gitlab的时候,发现下面这条语句不能用 git init --initial-branch XXX查看git version git version下载 wget https://mirrors.edge.kernel.org/pub/software/scm/git/git-2.38.1.tar.gz 或者 https://git-scm.com/download/linux 或者去github上面下载…...

测试C#调用Aplayer播放视频(2:VideoPlayer源码学习)
参考文献1除了介绍Aplayer组件的用法之外,还提供有demo下载以供学习,本文学习并记录其中的使用方式。 VideoPlayer项目使用C#在VS2013开发,其解决方案中包括VideoPlayer和VideoPlayer两个小项目,前者基于.net framework4.0&am…...

YOLOv5 分类模型的预处理
YOLOv5 分类模型的预处理 flyfish 版本 6.2 将整个代码简化成如下代码 imgsz224 file "/home/a/Pictures/1.jpg" transforms classify_transforms(imgsz) im cv2.cvtColor(cv2.imread(file), cv2.COLOR_BGR2RGB) print(im.shape)im transforms(im) print(im.…...

25 行为型模式-备忘录模式
1 备忘录模式介绍 备忘录模式(memento pattern)定义: 在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态. 2 备忘录模式原理 3 备忘录模式实现 /*** 发起人角色**/ public class Originator {private Strin…...

国防科技大学计算机基础课程笔记02信息编码
1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制,因此这个了16进制的数据既可以翻译成为这个机器码,也可以翻译成为这个国标码,所以这个时候很容易会出现这个歧义的情况; 因此,我们的这个国…...

【kafka】Golang实现分布式Masscan任务调度系统
要求: 输出两个程序,一个命令行程序(命令行参数用flag)和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽,然后将消息推送到kafka里面。 服务端程序: 从kafka消费者接收…...
ES6从入门到精通:前言
ES6简介 ES6(ECMAScript 2015)是JavaScript语言的重大更新,引入了许多新特性,包括语法糖、新数据类型、模块化支持等,显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var…...

练习(含atoi的模拟实现,自定义类型等练习)
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...
【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密
在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...
python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)
更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...
Linux云原生安全:零信任架构与机密计算
Linux云原生安全:零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言:云原生安全的范式革命 随着云原生技术的普及,安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测,到2025年,零信任架构将成为超…...

令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍
文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结: 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析: 实际业务去理解体会统一注…...
Java入门学习详细版(一)
大家好,Java 学习是一个系统学习的过程,核心原则就是“理论 实践 坚持”,并且需循序渐进,不可过于着急,本篇文章推出的这份详细入门学习资料将带大家从零基础开始,逐步掌握 Java 的核心概念和编程技能。 …...