什么是表达式计算器呢? 大家看下面的一个例子,就会明白
思路:实现的方式有很多中,大家都可以进行尝试,我这里使用的是List
-
为了避免错误的输入,我们先定义一个字符数组,该数组中存放所有正确的字符,只要出现某个字符不符合条件的,我们就判定为错误,并进行错误提示,所以我们要先校验表达式的准确性
-
我们把传入的字符串进行拆分,循环遍历这个表达式,如果是数字或者小数点,就将其添加到字符串缓冲区中,如果当前符号为运算符(+ - * /),就将符号前面的数据进行转换为Double类型的数据,并添加到我们的集合中
-
当然运算符是有优先级的 ()的优先级是最高的,如果遍历到‘’(‘’ 我们就截取剩下的字符串并为其匹配到右括号,匹配成功就将该子表达式调用当前方法得到结果,修改当前下标的值(计算过的数据我们不要重复计算,下标要进行改变使其指向下一个没有遍历的字符),加减乘除的乘和除的优先级相,如果连续出现 5*3/3*1*2 就采用从左到右的运算规则
-
使用switch语句来区分不同运算符,并做出对应的操作,如果运算符是+ ,就继续做添加的操作,如果是减,减法的操作其实就是加上对应的负数,减正就是加负,接下来要做的操作就是找到减号后边的元素(检索的重点是这个减号之后到下一个符号之前),并将其置为负数,添加到集合中。改变当前 i 的下标,增加刚才返回的数据的长度。如果当前符号是乘号,找到乘号前后的元素做运算,将结果保存到集合中(注:很重要!! 一定要删除乘号前边的数据,否则会重复计算),同时修改下标值。除法的操作类似于乘法,在做除法之前,要判断分母是否为0,并给出相应的提示
-
最后将集合中的所有数据相加得出最终结果
-
可以自定义异常,在调用出捕捉并进行提示
规范:
-
该功能实现方式不唯一,我们定义一个接口,接口中添加运算方法,并写一个实现类
-
我们定义一个执行器类,执行运算器。可以之进行一次运算,也可以一直循环进行运算。(这里我只用了一次运算,有循环执行的需求可以自己添加)
-
运算器的实现方式也有很多,比如双向链表,栈等等都可以实现。为了根据不同需求,定义一个工厂类,根据传入的参数不同返回不同的实例对象
程序执行流程
-
从工厂获取执行器,调用执行器的execute方法,传入参数
-
执行器中调用校验工具和运算器的运算方法
-
返回结果到调用处
代码:
运算器接口
public interface Calculator {
/**
* 运算接口
*/
double doOperation(String expression)throws MathException;
}
运算器实现类
public class CalculatorImplByList implements Calculator{
/**
* 对接口的运算方法实现
*/
@Override
public double doOperation(String expression) throws MathException {
if(!isDoExpression(expression))
return Double.parseDouble(expression);
List<Double> values = splitStr(expression);
double result=0;
for (Double value : values) {
result+=value;
}
return result;
}
/** 判断表达式是否包含运算符 '+' '-' '*' '/' */
private boolean isDoExpression(String expression) {
if(expression.contains("+")||expression.contains("-")||
expression.contains("*")||expression.contains("/"))
return true;
return false;
}
private List<Double> splitStr(String expression) throws MathException
{
int len = expression.length();
//拆分字符串
char[] chars = expression.toCharArray();
//定义一个List用于存放数据
List<Double> values = new ArrayList<Double>();
StringBuffer buf = new StringBuffer();
for(int i=0;i<chars.length-1;i++)
{
if(Character.isDigit(chars[i])||chars[i]=='.')
{
buf.append(chars[i]);
}
else
{
/*将符号前的数据添加到values*/
if(buf.length()!=0) {
values.add(Double.parseDouble(buf.toString()));
clearBuf(buf);
}
//10*310+5+6
switch (chars[i]) {
case '+':
if(chars[i+1]=='('||chars[i+1]=='(')
{
i++;
//返回括号内的表达式
String innerexp = innerOperation(expression.substring(i+1,len));
//调用程序入口计算该表达式
double res = this.doOperation(innerexp);
//计算结果存入集合
values.add(res);
//改变下标值
i+=innerexp.length()+1;
break;
}
double first = this.searchNext(expression.substring(i+1, len));
values.add(first);
i+=this.getLength(first);
break;
case '-':
if (chars[i + 1] == '(' || chars[i + 1] == '(') {
i++;
// 返回括号内的表达式
String innerexp = innerOperation(expression.substring(i + 1, len));
// 调用程序入口计算该表达式
double res = this.doOperation(innerexp);
// 计算结果存入集合
values.add(-res);
// 改变下标值
i += innerexp.length() + 1;
break;
}
if(values.size()==0)
{
double nex = searchNext(expression.substring(i+1, len));
values.add(-nex);
i+=this.getLength(nex);
}
else {
double temp = searchNext(expression.substring(i+1, len));
values.add(-temp);
clearBuf(buf);
i+=this.getLength(temp);
}
break;
case '*':
//拿到栈顶元素
double pre;
if(values.size()==0)
pre = searchPre(expression.substring(0, i));
else
pre = values.get(values.size()-1);
//拿到乘号后边的数据
double next;
if(chars[i+1]=='('||chars[i+1]=='(')
{
i++;
String res =innerOperation(expression.substring(i+1,len));
next=doOperation(res);
i+=res.length()+1;
}else {
next = searchNext(expression.substring(i+1, len));
i+=this.getLength(next);
}
values.add(pre*next);
values.remove(pre);
clearBuf(buf);
break;
case '/':
//拿到栈顶元素
double exceptPre;
if(values.size()==0)
exceptPre = searchPre(expression.substring(0, i));
else
exceptPre = values.get(values.size()-1);
//拿到乘号后边的数据
double exceptNext;
if(chars[i+1]=='('||chars[i+1]=='(')
{
i++;
String res1 =innerOperation(expression.substring(i+1,len));
exceptNext=doOperation(res1);
i+=res1.length()+1;
}else
{
exceptNext = searchNext(expression.substring(i+1, len));
i+=this.getLength(exceptNext);
}
if(exceptNext!=0)
values.add(exceptPre/exceptNext);
else
throw new MathException("表达式索引[ "+i+" ]处,分母不能为0");
values.remove(exceptPre);
clearBuf(buf);
break;
case '(':
if(i>=1&&String.valueOf(chars[i-1]).matches("\\d"))
throw new MathException("表达式格式错误");
//返回括号内的表达式
String innerexp = innerOperation(expression.substring(i+1,len));
//调用程序入口计算该表达式
double res = this.doOperation(innerexp);
//计算结果存入集合
values.add(res);
//改变下标值
i+=innerexp.length()+1;
break;
case '(':
if(i>=1&&String.valueOf(chars[i-1]).matches("\\d"))
throw new MathException("表达式格式错误");
String innerexp2 = innerOperation(expression.substring(i+1,len));
double res2 = this.doOperation(innerexp2);
values.add(res2);
i+=innerexp2.length()+1;
break;
default:
throw new MathException("表达式格式错误");
}
}
}
return values;
}
/** 括号内部的数据运算 */
private String innerOperation(String substr) throws MathException{
char[] arr = substr.toCharArray();
int left = 1;
int right= 0;
for(int i=0;i<arr.length;i++)
{
if(arr[i]=='('||arr[i]=='(')
{
left++;
}
if(arr[i]==')'||arr[i]==')') {
right++;
}
if(left==right)
{
return substr.substring(0, i);
}
}
throw new MathException("expect a ')' in your expression phase match '(' ");
}
/** 返回参数的长度 */
private int getLength(double value)
{
int tmp = (int) value;
if(tmp==value)
return String.valueOf(tmp).length();
else
return String.valueOf(value).length();
}
/** 查找当前符号的下一个数据 */
private double searchNext(String substr)
{
char[] arr = substr.toCharArray();
StringBuffer buf = new StringBuffer();
String result="0";
boolean flag = false;
for(int i=0;i<arr.length-1;i++)
{
if(Character.isDigit(arr[i])||arr[i]=='.'){
buf.append(arr[i]);
}
else {
result=buf.toString();
flag = true;
break;
}
}
if(flag==false)
return Double.parseDouble(substr);
else
return Double.parseDouble(result);
}
/** 查找当前符号的前一个数据 */
private double searchPre(String substr)
{
StringBuffer tmp = new StringBuffer(substr);
tmp=tmp.reverse();
char[] arr = tmp.toString().toCharArray();
StringBuffer buf = new StringBuffer();
String result="0";
boolean flag = false;
for(int i=0;i<arr.length-1;i++)
{
if(Character.isDigit(arr[i])||arr[i]=='.'){
buf.append(arr[i]);
}
else {
result=buf.reverse().toString();
flag=true;
break;
}
}
if(flag==false)
return Double.parseDouble(substr);
return Double.parseDouble(result);
}
/**清空 StringBuffer */
private void clearBuf(StringBuffer buf)
{
buf.delete(0, buf.length());
}
}
执行器
public class Executor {
private Scanner scanner = new Scanner(System.in);
/**
* call this method can execute operation
* @param expression
* @return
*/
public double excute(String calulatorClassName)
{
System.out.println("Please input the Arithmetic expression:");
String expression = scanner.nextLine();
double dResult = 0;
try {
expression = CheckOut.check(expression.trim());
dResult = BeanFactory.getCalculator(calulatorClassName).doOperation(expression);
} catch (Exception e) {
System.err.println(e.getMessage()+", 请重新输入");
/** 递归当前方法 */
return excute(calulatorClassName);
}finally {
/** 关闭流 */
scanner.close();
}
return dResult;
}
}
表达式检验工具
public class CheckOut {
private static char opes[]= {'.','0','1','2','3','4','5','6','7','8','9','+','-','*','/','(',')','(',')'};
public static String check(String expression) throws ExpressionException{
/*判断输入的表达式是否符合规范*/
char[] chars = expression.toCharArray();
for(int i=0;i<chars.length;i++)
{
if(!containsOpe(chars[i]))
{
throw new ExpressionException("输入格式错误");
}
}
return expression;
}
/*判断该字符是否属于opes*/
private static boolean containsOpe(char ch) {
if(ch==' ')
return false;
for(int i=0;i<opes.length;i++)
{
if(opes[i]==ch)
return true;
}
return false;
}
}
工厂类
package cn.edu.aynu.calculator.util;
import cn.edu.aynu.calculator.core.Calculator;
import cn.edu.aynu.calculator.core.Executor;
public class BeanFactory {
/**
* 工厂类用来获取单例的Bean实例
* you can get some instances such as
* calculator executor checkOut ....
*/
/**
* calculator 运算器
*/
private static Calculator calculator;
/**
* executor执行器
*/
private static Executor executor;
/**
* checkOut表达式检验器
*/
private static CheckOut checkOut;
/**
* the method can get the Type of Calculator Impel instance.
* @return
* @throws ClassNotFoundException
* @throws IllegalAccessException
* @throws InstantiationException
*/
public static Calculator getCalculator(String calulatorClassName) throws InstantiationException, IllegalAccessException, ClassNotFoundException
{
if(calculator==null)
calculator = (Calculator) Class.forName(calulatorClassName).newInstance();
return calculator;
}
/**
* the method can get the Type of Executor instance.
* @return
*/
public static Executor getExecutor()
{
if(executor==null)
executor = new Executor();
return executor;
}
/**
* the method can get the Type of expression CheckOut instance.
* @return
*/
public static CheckOut getCheckout()
{
if(checkOut==null)
checkOut = new CheckOut();
return checkOut;
}
}
主函数
public class test {
public static void main(String[] args) {
String calulatorClassName = "cn.edu.aynu.calculator.core.CalculatorImplByList";
/**
* get the Calculator Executor By BeanFactory
*/
double dResult = BeanFactory.getExecutor().excute(calulatorClassName);
System.out.print("the result is ");
System.out.println(dResult == (int)dResult ? (int)dResult : String.format("%.2f", dResult));
}
}