java实现中缀表达式计算器

什么是表达式计算器呢? 大家看下面的一个例子,就会明白


思路:实现的方式有很多中,大家都可以进行尝试,我这里使用的是List

  1. 为了避免错误的输入,我们先定义一个字符数组,该数组中存放所有正确的字符,只要出现某个字符不符合条件的,我们就判定为错误,并进行错误提示,所以我们要先校验表达式的准确性

  2. 我们把传入的字符串进行拆分,循环遍历这个表达式,如果是数字或者小数点,就将其添加到字符串缓冲区中,如果当前符号为运算符(+ - * /),就将符号前面的数据进行转换为Double类型的数据,并添加到我们的集合中

  3. 当然运算符是有优先级的 ()的优先级是最高的,如果遍历到‘’(‘’ 我们就截取剩下的字符串并为其匹配到右括号,匹配成功就将该子表达式调用当前方法得到结果,修改当前下标的值(计算过的数据我们不要重复计算,下标要进行改变使其指向下一个没有遍历的字符),加减乘除的乘和除的优先级相,如果连续出现 5*3/3*1*2 就采用从左到右的运算规则

  4. 使用switch语句来区分不同运算符,并做出对应的操作,如果运算符是+ ,就继续做添加的操作,如果是减,减法的操作其实就是加上对应的负数,减正就是加负,接下来要做的操作就是找到减号后边的元素(检索的重点是这个减号之后到下一个符号之前),并将其置为负数,添加到集合中。改变当前  i 的下标,增加刚才返回的数据的长度。如果当前符号是乘号,找到乘号前后的元素做运算,将结果保存到集合中(注:很重要!! 一定要删除乘号前边的数据,否则会重复计算),同时修改下标值。除法的操作类似于乘法,在做除法之前,要判断分母是否为0,并给出相应的提示

  5. 最后将集合中的所有数据相加得出最终结果

  6. 可以自定义异常,在调用出捕捉并进行提示


规范:

  1. 该功能实现方式不唯一,我们定义一个接口,接口中添加运算方法,并写一个实现类

  2. 我们定义一个执行器类,执行运算器。可以之进行一次运算,也可以一直循环进行运算。(这里我只用了一次运算,有循环执行的需求可以自己添加)

  3. 运算器的实现方式也有很多,比如双向链表,栈等等都可以实现。为了根据不同需求,定义一个工厂类,根据传入的参数不同返回不同的实例对象


程序执行流程

  1. 从工厂获取执行器,调用执行器的execute方法,传入参数

  2. 执行器中调用校验工具和运算器的运算方法

  3. 返回结果到调用处


代码:

运算器接口


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));
	}	
}

猜你喜欢

转载自blog.csdn.net/Lemon_Trees/article/details/82805488