接着《后缀表达式与解析算术表达式》一文。扩充程序,让他支持整数、小数。会加减乘除操作,这就是一个迷你型的计算器程序了(我的华为手机就是这个程序)。
首先,我们要定义一个基本对象类,他用来存储是操作符和操作数。其代码如下:
import java.math.BigDecimal;
public class BaseObject {
//数字,字符。当charData='m'时,表示BaseObject为数字
private BigDecimal numData = new BigDecimal("0");
private char charData = 'm';
public BigDecimal getNumData() {
return numData;
}
public void setNumData(BigDecimal numData) {
this.numData = numData;
}
public char getCharData() {
return charData;
}
public void setCharData(char charData) {
this.charData = charData;
}
//方法重载,数字
public BaseObject(String numStr)
{
numData = new BigDecimal(numStr);
charData = 'm';
}
//方法重载,操作符
public BaseObject(char oper)
{
charData = oper;
}
}
然后,在计算器中。我们使用Java.util包中的栈类stack,另外也将转化器和计算器合并,其代码如下:
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
public class SimpCalc {
private Stack<BaseObject> operStack;
private List<BaseObject> cvtExp = new ArrayList<BaseObject>();
public BigDecimal calc(String exp) throws Exception{
initCalc(exp);
List<BaseObject> list = analysis(exp);
convert(list);
return calcCore();
}
private void initCalc(String expStr)
{
operStack = new Stack<BaseObject>();
}
//解析输入中缀表达式
private List<BaseObject> analysis(String expStr) throws Exception
{
int numStart=-1;
int numEnd=-1;
List<BaseObject> exp = new ArrayList<BaseObject>();
for (int i = 0; i < expStr.length(); i++) {
char ca = expStr.charAt(i);
if(ca >= '0' && ca <= '9')
{
if(numStart == -1)
numStart = i;
else
numEnd = i;
}
else if(ca == '+' || ca == '-' || ca == '*' || ca == '/' || ca == '(' || ca == ')')
{
numEnd = i-1;
if(numStart != -1)
exp.add(new BaseObject(expStr.substring(numStart, numEnd+1)));
exp.add(new BaseObject(ca));
numStart = -1;
}
else
throw new Exception("输入包括非法字符。只能输入数字、括号和+-*/");
}
if(numStart != -1)
exp.add(new BaseObject(expStr.substring(numStart)));
return exp;
}
//转换
private void convert(List<BaseObject> exp)
{
for (int i = 0; i < exp.size(); i++) {
BaseObject eo = exp.get(i);
char cr = exp.get(i).getCharData();
switch (cr) {
case '+':
forOper(eo,cr,1);
break;
case '-':
forOper(eo,cr,1);
break;
case '*':
forOper(eo,cr,2);
break;
case '/':
forOper(eo,cr,2);
break;
case '(':
operStack.push(exp.get(i));
break;
case ')':
forBra();
break;
default:
cvtExp.add(exp.get(i));
break;
}
}
while(!operStack.isEmpty()){
cvtExp.add(operStack.pop());
}
}
private void forBra() {
while(!operStack.isEmpty())
{
BaseObject eo = operStack.pop();
char topOper = eo.getCharData();
if(topOper == '(')
break;
else
cvtExp.add(eo);
}
}
private void forOper(BaseObject thisEo, char thisOper, int thisWt)
{
while(!operStack.isEmpty())
{
BaseObject topEo = operStack.pop();
char topOper = topEo.getCharData();
if(topOper == '(')
{
operStack.push(topEo);
break;
}
else
{
int topWt = 2;
if(topOper == '+' || topOper =='-')
topWt = 1;
if(topWt < thisWt)
{
operStack.push(topEo);
break;
}
else
cvtExp.add(topEo);
}
}
operStack.push(thisEo);
}
//计算
private BigDecimal calcCore()
{
BigDecimal num1 = new BigDecimal("0");
BigDecimal num2 = new BigDecimal("0");
BigDecimal result = new BigDecimal("0");
Stack<BigDecimal> numStack = new Stack<BigDecimal>();;
for (int i = 0; i < cvtExp.size(); i++)
{
BaseObject eo = cvtExp.get(i);
char cr = eo.getCharData();
if(cr == 'm')
{
numStack.push(eo.getNumData());
}
else
{
num1 = numStack.pop();
num2 = numStack.pop();
switch (cr) {
case '+':
result = num2.add(num1);
break;
case '-':
result = num2.subtract(num1);
break;
case '*':
result = num2.multiply(num1);
break;
case '/':
result = num2.divide(num1,4,BigDecimal.ROUND_HALF_DOWN);//保留4位小数
break;
default:
result = new BigDecimal("0");
break;
}
numStack.push(result);
}
}
return numStack.pop();
}
//测试
public static void main(String[] args) throws Exception {
InputStreamReader isr = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(isr);
SimpCalc sc = new SimpCalc();
while(true)//可持续输入
{
String str = br.readLine();
System.out.println(sc.calc(str));
}
}
}
代码虽然修改不少,但是大家可以看出,逻辑基本没有变化,所以只要理解了《后缀表达式与解析算术表达式》一文。本文就容易了。
还有就是有点遗憾,没有处理负数,如果大家有时间可以试试。
最后,我们可以试试,迷你计算器:
16-3*(6-(4/2))
4.0000
4*4
16
9/2
4.5000