Java实现四则运算,使用堆栈,检查语法
突然发闲想试一试自己实现算术的四则运算,支持加减乘除和括号、正负号;支持语法检查;思路很常规,利用两个堆栈,一个压操作符,一个压操作数,念头冒出来之后,立马动手;然后本以为很容易的一个实现,却存在各种各样的坑,正常逻辑花了1个小时,填坑缺填了5个小时,不多说,上代码;
能够检测的语法错误:缺少操作数、缺少操作符、缺失括号、不合法的数值;
支持运算程度:全部使用浮点数float;支持任意位置的空格、制表符、回车;多重括号;
视为语法错误的约束:空括号、多重正负号(非加减号)、除数为0;
编码能力有限,望各路大神海涵;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack; public class FunctionStack { private Map<String, Integer> optLevel;
private boolean nextIsOpt = false;
private boolean debug = false;
private int debug_len = 3; public FunctionStack setDebugLen(int len) {
this.debug_len = len;
return this;
} public FunctionStack setDebug(boolean debug) {
this.debug = debug;
return this;
} private void println(Object obj) {
if (debug) {
System.out.println(obj);
}
} public FunctionStack() {
optLevel = new HashMap<String, Integer>();
optLevel.put("(", 1);
optLevel.put("+", 2);
optLevel.put("-", 2);
optLevel.put("*", 3);
optLevel.put("/", 3);
optLevel.put(")", 4);
} public static void main(String[] args) { FunctionStack fs = new FunctionStack();
fs.setDebug(true).setDebugLen(5);
String fun = " 1 + 2 * 3 / 4 ";
try {
float res = fs.execute(fun);
System.out.println("结果为:" + res);
} catch (FunctionStackException e) {
System.out.println(e.getMessage());
}
System.out.println("结束.");
} /**
*
* @param fun
* @return
* @throws FunctionStackException
*/
public float execute(String fun) throws FunctionStackException {
this.clear();
// TODO Auto-generated method stub
if (fun == null || fun.trim().length() == 0) {
throw new FunctionStackException("表达式不能为空;");
}
// 创建操作符堆栈和操作数堆栈
Stack<String> opt = new Stack<String>();
Stack<Float> num = new Stack<Float>();
// 扫描整个表达式
// point记录上一个扫描点
int point = 0;
for (int i = 0; i < fun.length(); i++) {
String scanOpt = fun.charAt(i) + "";
if (scanOpt.equals("(")) {
// 发现左括号,压入栈
// 检查是否为空的括号
int right = fun.indexOf(')', i);
if (right < 0) {
// 如果没有找到右括号
throw new FunctionStackException(fun
+ "\n语法错误:"
+ "缺少右括号与之对应:"
+ fun.substring(Math.max(point - debug_len, 0),
Math.min(i + debug_len, fun.length())));
} else {
// 找到右括号,检查是否为空括号
if (fun.substring(i + 1, right).trim().length() == 0) {
throw new FunctionStackException(fun
+ "\n语法错误:"
+ "括号中内容不可为空:"
+ fun.substring(Math.max(point - debug_len, 0),
Math.min(i + debug_len, fun.length())));
}
}
// 检查语法
String num_before_opt = fun.substring(point, i).trim();
if (num_before_opt.length() != 0) {
throw new FunctionStackException(fun
+ "\n语法错误:"
+ "括号前缺少操作符:"
+ fun.substring(Math.max(point - debug_len, 0),
Math.min(i + debug_len, fun.length())));
}
// 将左括号压入栈
println("↓压栈:" + fun.substring(i, i + 1));
opt.push(fun.substring(i, i + 1));
// 记录扫描点
point = i + 1;
} else if (scanOpt.equals(")")) {
// 发现右括号,取出栈,直到取出左括号
println("---计算括号开始:");
// 将括号前的数值取出
pushFloatStack(fun, num, point, i);
// 记录下一个是操作符
nextIsOpt = true;
// 检查前一个操作符是否为空
if (opt.empty()) {
throw new FunctionStackException(fun
+ "\n语法错误:"
+ "缺少左括号与之对应:"
+ fun.substring(Math.max(point - debug_len, 0),
Math.min(i + debug_len, fun.length())));
}
// 取出前一个操作符
String optpop = opt.pop();
println("↑↑出栈:" + optpop);
// 取出栈,直到取出左括号
while (!optpop.equals("(")) {
// 若取出的操作符不是左括号,执行运算;
calculator(optpop, num);
if (opt.empty()) {
throw new FunctionStackException(fun
+ "\n语法错误:"
+ "缺少左括号与之对应:"
+ fun.substring(Math.max(point - debug_len, 0),
Math.min(i + debug_len, fun.length())));
}
optpop = opt.pop();
println("↑↑出栈:" + optpop);
} // 记录扫描点
point = i + 1;
println("---计算括号结束:");
} else if (optLevel.get(scanOpt) != null) {
if (scanOpt.equals("-") || scanOpt.equals("+")) {
// 如果是减号,可能是一个负号
if (fun.substring(point, i).trim().replaceAll("-", "")
.replaceAll("\\+", "").trim().length() == 0) {
// 如果减号前没有操作数,视此减号为负号,point不移动,-也不压入堆栈
continue;
}
}
// 发现非括号的操作符,查看栈顶操作符优先级,选择计算or压栈
// 获取操作数,并检查语法
pushFloatStack(fun, num, point, i);
// 比较优先级,将栈顶优先级高的先计算
if (!opt.empty()) {
// 获取栈顶操作符,不取出
String optpop = opt.peek();
// 取栈计算,直到栈顶操作符优先级小于scanOpt
while (optLevel.get(optpop) >= optLevel.get(scanOpt)) {
calculator(optpop, num);
optpop = opt.pop();
println("↑↑出栈:" + optpop);
if (opt.empty()) {
// 如果操作符取空了则退出
break;
}
optpop = opt.peek();
}
}
// 压入操作符
println("↓压栈:" + scanOpt);
opt.push(scanOpt);
// 记录扫描点
point = i + 1;
} else if (scanOpt.equals("=")) {
// 发现=号,取栈计算总结果,并提前结束循环
// 获取操作数,并检查语法
pushFloatStack(fun, num, point, i);
// 取栈计算直到结束
while (!opt.empty()) {
String optpop = opt.pop();
println("↑↑出栈:" + optpop);
calculator(optpop, num);
}
return getResult(num);
} } // 表达式结束
// 获取操作数,并检查语法
pushFloatStack(fun, num, point, fun.length());
// 取栈计算直到结束
while (!opt.empty()) {
String optpop = opt.pop();
println("↑↑出栈:" + optpop);
calculator(optpop, num);
}
return getResult(num);
} private void clear() {
// TODO Auto-generated method stub
nextIsOpt = false;
} private float getResult(Stack<Float> num) {
// TODO Auto-generated method stub
Float res = num.pop();
if (num.empty()) {
return res;
} else {
throw new FunctionStackException("计算错误,堆栈中还有数据;");
}
} /**
* 将操作符i前的操作数解析,并压入堆栈
*
* @param fun
* @param num
* @param point
* 操作数起点
* @param i
* 操作符位置,即操作数终点
*/
private void pushFloatStack(String fun, Stack<Float> num, int point, int i) {
String num_before_opt = fun.substring(point, i).trim();
if (num_before_opt.length() == 0) {
// 没有操作数
if (nextIsOpt) { // 应该没有操作数,(即此处本应只有操作符,没有操作数)
nextIsOpt = false;
return;
} else {
// 应该有操作数
throw new FunctionStackException(fun
+ "\n语法错误:"
+ "缺少操作数:"
+ fun.substring(Math.max(point - debug_len, 0),
Math.min(i + debug_len, fun.length())));
}
} else {
// 有操作数
if (nextIsOpt) {
// 应该没有操作数
throw new FunctionStackException(fun
+ "\n语法错误:"
+ "缺少操作符:"
+ fun.substring(Math.max(point - debug_len, 0),
Math.min(i + debug_len, fun.length())));
} else {
// 应该有操作数
try {
// 去除操作数中间的空格、回车、制表符
num_before_opt = num_before_opt.replaceAll(" ", "");
num_before_opt = num_before_opt.replaceAll("\t", "");
num_before_opt = num_before_opt.replaceAll("\n", "");
Float scannum = Float.parseFloat(num_before_opt);
println("↓压栈:" + scannum);
num.push(scannum);
} catch (NumberFormatException e) {
throw new FunctionStackException(fun + "\n语法错误:"
+ "无法识别的数值:" + num_before_opt);
}
}
}
} /**
*
* @param optpop
* 运算符
*/
private void calculator(String optpop, Stack<Float> num) {
// TODO Auto-generated method stub
Float pop2 = num.pop();
println("↑↑出栈:" + pop2);
Float pop1 = num.pop();
println("↑↑出栈:" + pop1);
println("--------计算 " + pop1 + optpop + pop2);
if (optpop.equals("+")) {
println("↓压栈:" + (pop1 + pop2));
num.push(pop1 + pop2);
} else if (optpop.equals("-")) {
println("↓压栈:" + (pop1 - pop2));
num.push(pop1 - pop2);
} else if (optpop.equals("*")) {
println("↓压栈:" + (pop1 * pop2));
num.push(pop1 * pop2);
} else if (optpop.equals("/")) {
if (pop2 == 0) {
throw new FunctionStackException("语法错误:" + "除数不可以为零:" + pop2);
}
println("↓压栈:" + (pop1 / pop2));
num.push(pop1 / pop2);
} else if (optpop.equals("(")) {
throw new FunctionStackException("语法错误:" + "缺少右括号与之对应:" + optpop);
} else {
throw new FunctionStackException("语法错误:" + "错误的操作符:" + optpop);
} } }
主代码
public class FunctionStackException extends RuntimeException { /**
*
*/
private static final long serialVersionUID = 1L; public FunctionStackException(String message) {
super(message);
} }
自定义异常
Java实现四则运算,使用堆栈,检查语法的更多相关文章
- [转] Java程序员学C#基本语法两个小时搞定(对比学习)
Java程序员学C#基本语法两个小时搞定(对比学习) 对于学习一门新的语言,关键是学习新语言和以前掌握的语言的区别,但是也不要让以前语言的东西,固定了自己的思维模式,多看一下新的语言的编程思想. ...
- 使用TCMalloc的堆栈检查
在前一篇译文<TCMalloc:线程缓冲的Malloc>详细讲解了TCMalloc的工作原理和特点,今天翻译<heap-checking using tcmalloc>,了解T ...
- Java和C#在面向对象上语法的区别
做了几年了开发一直没有总结什么,回到了家乡的小城做了一名培训班的教员,教授软件开发的知识.细小的知识从头细细嚼来,别有一番滋味.或是以前遗漏的太多,或是确实没有系统的学习过,教学生的过程中自己也对教材 ...
- Java正則表達式语法
Java正則表達式语法 字符 说明 \ 将下一字符标记为特殊字符.文本.反向引用或八进制转义符.比如,"n"匹配字符"n"."\n"匹配换行 ...
- java中堆和堆栈的区别
java中堆和堆栈的区别(一) 1.栈(stack)与堆(heap)都是Java用来在Ram中存放数据的地方.与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆. 2. 栈的优势是,存取 ...
- 使用 java 实现一个简单的 markdown 语法解析器
1. 什么是 markdown Markdown 是一种轻量级的「标记语言」,它的优点很多,目前也被越来越多的写作爱好者,撰稿者广泛使用.看到这里请不要被「标记」.「语言」所迷惑,Markdown 的 ...
- java中存储机制堆栈。
一.java的六种存储地址及解释 1) 寄存器(register):这是最快的存储区,因为它位于不同于其他存储区的地方——处理器内部.但是寄存器的数量极其有限,所以寄存器由编译器根据需求进行分配.你不 ...
- Java多线程——查看线程堆栈信息
Java多线程——查看线程堆栈信息 摘要:本文主要介绍了查看线程堆栈信息的方法. 使用Thread类的getAllStackTraces()方法 方法定义 可以看到getAllStackTraces( ...
- Java入门 - 语言基础 - 03.基础语法
原文地址:http://www.work100.net/training/java-basic-syntax.html 更多教程:光束云 - 免费课程 基础语法 序号 文内章节 视频 1 第一个Jav ...
随机推荐
- Spring Framework------>version4.3.5.RELAESE----->Reference Documentation学习心得----->关于spring framework中的beans
Spring framework中的beans 1.概述 bean其实就是各个类实例化后的对象,即objects spring framework的IOC容器所管理的基本单元就是bean spring ...
- HDU2063 过山车
过山车 RPG girls今天和大家一起去游乐场玩,终于可以坐上梦寐以求的过山车了.可是,过山车的每一排只有两个座位,而且还有条不成文的规矩,就是每个女生必须找个个男生做partner和她同坐.但是, ...
- linux 系统服务
此文涉及的命令:service.chkconfig. 概念 daemon 的主要分类 stand_alone:此 daemon 可以自行单独启动服务 属性:daemon 启动并加载到内存后就一直占用内 ...
- C# 去除字符串首尾字符或字符串
在做一个属性入库的功能,将Excel属性数据导入到图层要素当中,这里Excel和SDE数据库数据存在一个关联字段,通过关联字段值进行匹配属性入库. 在实际业务中,由于普查数据往 ...
- Hishop网站迁移后出现DataProtectionConfigurationProvider错误
错误代码如下: 配置错误 说明: 在处理向该请求提供服务所需的配置文件时出错.请检查下面的特定错误详细信息并适当地修改配置文件. 分析器错误信息: 未能使用提供程序“DataProtectionCon ...
- 怎样获取本机的ip地址
首先介绍一下用到的结构体 struct hostent { const char *h_name; // official name of host char **h_aliases; // alia ...
- 解决jquery1.9不支持browser对象的问题||TypeError: $.browser is undefined
在插件的源代码里插入如下代码: (function(jQuery){ if(jQuery.browser) return; jQuery.browser = {}; jQuery.browser.mo ...
- Runloop 深入理解(转)
RunLoop 是 iOS 和 OSX 开发中非常基础的一个概念,这篇文章将从 CFRunLoop 的源码入手,介绍 RunLoop 的概念以及底层实现原理.之后会介绍一下在 iOS 中,苹果是如何利 ...
- (shell )函数
一.定义格式 [function] 函数名() { 命令表 } 二.调用方法 先定义,后使用,直接输入函数名,不需要圆括号() 三.函数参数传递方法 可以利用位置参数或者变量进行传递 #! /bin/ ...
- Log
Public Shared Sub WriteLog(ByVal FormID As String, ByVal ex1 As Exception) 'log信息 Dim loginfo As Str ...