该代码是字符串输入字符串返回,本身开发是应用于安卓应用,所以没有设计图形界面,完全是逻辑计算,开始是打算一个方法写到底的,但是写起来不容易读,所以重构了一次,主要数据类型用的是链表(没有使用Stack运算),对是我故意的,虽然代码不怎么好,但每个单元还是做了单元测试的,暂时还没发现bug,但不保证没有。把注释去了刚好400行,我数过了,应该不算多,当然里面部分链表使用了for i循环,虽然影响性能,但转换也挺费性能的,部分能转的也转了foreach。但考虑到计算器能的消耗,就不用麻烦自己了。
package com.ca;
import java.math.BigDecimal;
import java.util.LinkedList;
import static java.util.regex.Pattern.matches;
/**
* 实现带括号和小数计算运算
* 优先运算符号和数学方法一致
* 小数最多保留8位
* 小数只有有效精度
*/
public class Caculator {
//遍历数组时不能改变数组长度,这里记录需要添加的外层括号数量
private int pdCountS = 0;
private int pdCountE = 0;
/**
* 对外接口
* @param string 字符串类型的公式,英文符号,不能使用空格
* @return 结果以字符串类型返回
*/
public static String run(String string){
return new Caculator().start(string);
}
/**
* 主方法入口
* @param string 从唯一接口获取数据
* @return 结果返回接口
*/
private String start(String string){
LinkedList<String> asp = conversion(string);
asp=pdDeal(asp);
for (int i = 0; i <asp.size() ; i++) {
if (matches(".*\\(.*", asp.get(i))) {
asp=replace(asp);
i=-1;
}
}
return read(asp);
}
/**
* @param string 控制器接收的字符串类型
* @return LinkedList<String> 下级
*/
private LinkedList<String> conversion(String string) { //字符串转链表
LinkedList<String> asm = new LinkedList<>();
LinkedList<String> as2 = new LinkedList<>();
String[] arr1 = string.split("\\+|-|\\*|/|\\(|\\)"); //读取String数字部分
String[] arr2 = string.split("\\d|\\."); //读取String运算符部分
for (String i : arr1) { //清空空值
if (!"".equals(i)) {
asm.add(i);
}
}
for (String i : arr2) { //清空空值
if (!"".equals(i)) {
if (matches(".*[() ].*+", i) ) {
System.err.println("输入格式错误:本机不接受非英文字符和括号");
System.exit(1);
}
as2.add(i);
}
}
try {
if (!matches("^\\(.+|\\(", as2.get(0))) { //数字和运算符合并,开头不是“(”时
for (int i = 0; i < as2.size(); i++) {
asm.add(2 * i + 1, as2.get(i));
}
return asm;
}
for (int i = 0; i < as2.size(); i++) { //数字和运算符合并,开头为数字时
asm.add(2 * i, as2.get(i));
}
}catch (IndexOutOfBoundsException ex){
System.err.println("输入异常:请输入正确符号,不要输入英文类型的.()运算符和数字以外的字符");
System.exit(1);
}
return asm;
}
/**
* 读取计算无括号内容
* @param as1 任何输入的无空格链表
* @return 字符串类型结果
*/
private String read(LinkedList<String> as1){
int size =0;
String result = "";
String start = "";
String end = "";
String nums = "";
String nume = "";
for (String i:as1) {
if ( !matches("\\D", i)){ //String为数字时
if ("".equals(nums)) {
nums=i;
}
else nume=i;
}
else if ("".equals(start)) { //String为运算符时
start=i;
}
else end=i;
size++;
if (!"".equals(end) ||size==as1.size()) {
result=judgmentC(start,nums,nume);
start = end;
end ="";
nume="";
nums=String.valueOf(result);
}
}
return result;
}
/**
* 四则运算
* @param start 运算符
* @param nums 运算符左侧数字
* @param nume 运算符右侧数字
* @return 字符串类型结果
*/
private String judgmentC(String start, String nums, String nume) {
BigDecimal b1 = new BigDecimal(nums);
BigDecimal b2 = new BigDecimal(nume);
String re="";
if ("+".equals(start)) {
re = b1.add(b2).toString();
}
if ("-".equals(start)) {
re =b1.subtract(b2).toString();
}
if ("*".equals(start)) {
re =b1.multiply(b2).toString();
}
if ("/".equals(start)) {
re =b1.divide(b2,8, BigDecimal.ROUND_HALF_EVEN).toString();
String div = "";
int len = re.length();
int lencount = 0;
for (int i = len-1; i > 0; i--) {
if (re.charAt(i)=='0') {
lencount++;
continue;
}
break;
}
for (int i = 0; i < len-lencount; i++) {
div += re.charAt(i);
if (re.charAt(len-lencount-1) == '.') {
break;
}
}
re = div;
}
return re;
}
/**
* 括号内优先运算
* @param asm Tset1单元传入带括号
* @return 消除括号后返回括号内结果
*/
private LinkedList<String> replace(LinkedList<String> asm){
LinkedList<String> as2 = new LinkedList<>();
int count1 = 0; //( 号的数量
int count2 = 0; // )号的数量
int key1 = 0; //锁1,第一个括号处理完后打开
int key2 = 0; //锁2,检测到第一个括号时打开
int index=0; //指针,第一个 ( 括号出现时对应的i位置
//解最外圈括号并添加到as2
for (int i = 0; i < asm.size()-1; i++) {
if (key2 != 1 && matches(".*\\(.*", asm.get(i))) {
key2=1;
index=i;
}
//静态锁2打开后开始添加到as2
if (key2 == 1) {
as2.add(asm.get(i));
}
//拆外圈括号
if (matches(".*\\(.*", asm.get(i))) {
int cmt1=0;
for (int j = 0; j < asm.get(i).length(); j++) {
if (asm.get(i).charAt(j) == '(') {
cmt1++;
}
}
count1 += cmt1;
if (key1 != 1) {
asm.set(i, asm.get(i).replaceFirst("\\(", ""));
}
key1=1;
}
if (matches(".*\\).*", asm.get(i + 1))) {
int cmt1=0;
int a=asm.get(i+1).length();
for (int j = 0; j < a; j++) {
if (asm.get(i+1).charAt(j) == ')') {
cmt1++;
}
}
count2 += cmt1;
if (count1 == count2) {
as2.add(asm.get(i+1));
asm.set(i + 1, asm.get(i + 1).replaceFirst("\\)", ""));
}
}
if (count1 == count2 && count1 != 0) {
int index2=0;
if (matches(".*\\(.*",as2.get(0))) {
int countS = 0;
int countE = 0;
for (int j = 0; j < as2.get(0).length(); j++) {
if (as2.get(0).charAt(j)=='(') {
countS++;
}
}
for (int j = 0; j < as2.get(as2.size()-1).length(); j++) {
if (as2.get(as2.size()-1).charAt(j)==')') {
countE++;
}
}
if (countE > 1 && countS > 1) {
return asm;
}
}
if ("".equals(asm.get(asm.size()-1))) {
asm.remove(asm.size()-1);
}
if ("".equals(asm.get(0))) {
asm.remove(0);
index2=1;
}
if (asm.size() < as2.size()) {
return asm;
}
if (as2 !=null) {
asm=asmReduce(asm,as2,index,index2);
return asm;
}
break;
}
}
if ("".equals(asm.get(asm.size()-1))) {
asm.remove(asm.size()-1);
}
return asm;
}
/**
* 仅供replace方法调用
* 收缩准备返回的主链表
* @param asm replace方法中的asm链表
* @return 修改后返回asm链表
*/
private LinkedList<String> asmReduce(LinkedList<String> asm,LinkedList<String> as2,int index,int index2){
for (int j = 1; j <as2.size()-1-index2; j++) {
asm.remove(index+1-index2);
}
if (matches(".*\\(.*", asm.get(index))) {
asm.set(index, asm.get(index).replaceAll("\\(", ""));
}
if (asm.size()>index+1 && matches(".*\\).*", asm.get(index+1))) {
asm.set(index+1, asm.get(index+1).replaceAll("\\)", ""));
}
as2=as2Reduce(as2);
for (int j = 0; j < as2.size(); j++) {
if (matches(".*\\(.*", as2.get(j))) {
as2=replace(as2);
if (index+1-index2==asm.size()) {
asm.add(read(as2));
return asm;
}
if ("".equals(asm.get(0))) {
asm.set(0,read(as2));
return asm;
}
if (index+1-index2>=asm.size() || matches(".*\\d.*||.* .*", asm.get(index+1-index2))) {
asm.set(index+1-index2,read(as2));
}
else {
asm.add(index+1-index2,read(as2));
}
return asm;
}
}
if (index+1-index2 >= asm.size()) {
asm.add(read(as2));
}
else if (index+1-index2 ==0) {
asm.set(index+1-index2,read(as2));
}
else {
asm.add(index+1-index2,read(as2));
}
return asm;
}
/**
* 仅供replace方法调用
* as2为需要计算的括号内部分
* @param as2 replace 中的as2
* @return 返回下一步结果
*/
private LinkedList<String> as2Reduce(LinkedList<String> as2){
if (matches(".*\\).*", as2.get(as2.size()-1))) {
as2.set(as2.size()-1, as2.get(as2.size()-1).replaceFirst("\\)", ""));
as2.set(as2.size()-1, as2.get(as2.size()-1).replaceAll("[^)]", ""));
}
if (!matches(".*\\).*", as2.get(as2.size()-1))) {
as2.remove(as2.size()-1);
}
if (matches(".*\\(.*", as2.get(0))) {
as2.set(0, as2.get(0).replaceFirst("\\(", ""));
as2.set(0, as2.get(0).replaceAll("[^(]", ""));
}
if (!matches(".*\\(.*", as2.get(0))) {
as2.remove(0);
}
return as2;
}
/**
* 对/*号提供括号实现优先级
* @param asp conversion方法后
* @return 添加括号后返回
*/
private LinkedList<String> pdDeal(LinkedList<String> asp){
int counti =0; //记录每次循环的对应位置
int count1 = 0; //记录(数
int count2 = 0; //记录)数
for (String i:asp) {
if (matches(".*\\*.*|.*/.*", i)){
if (matches(".*\\).*", i)){
for (int j = 0; j <i.length() ; j++) {
if (i.charAt(j)==')') {
count1++;
}
}
}
if (matches(".*\\(.*", i)){
for (int j = 0; j <i.length() ; j++) {
if (i.charAt(j)=='(') {
count2++;
}
}
}
asp = pdAdd(asp,counti,count1,count2);
count1=0;
count2=0;
}
counti++;
}
String end ="";
String start ="";
for (int i = 0; i < pdCountS; i++) {
start +="(";
}
for (int i = 0; i < pdCountE; i++) {
end +=")";
}
if (!"".equals(start)) {
asp.add(0,start);
}
if (!"".equals(end)) {
asp.add(end);
}
return asp;
}
/**
* 仅供pdeal方法调用
* @param asp 传递链表
* @param counti 优先级符号出现的位置
* @param count1 该位置已有的)号数量
* @param count2 该位置已有的(号数量
* @return 返回添加距哦好后结果
*/
private LinkedList<String> pdAdd(LinkedList<String> asp,int counti,int count1,int count2){
int countL = 0;
int countR = 0;
int aLi = 0;
int aRi = 0;
for (int i = counti-1; i >= 0; i--) {
if (matches(".*\\(.*", asp.get(i))){
for (int j = 0; j <asp.get(i).length() ; j++) {
if (asp.get(i).charAt(j)=='(') {
countL++;
}
if (count1 == countL) {
aLi=1;
break;
}
}
}
if (aLi != 1 && matches(".*\\).*", asp.get(i))){
for (int j = 0; j <asp.get(i).length() ; j++) {
if (asp.get(i).charAt(j)==')') {
count1++;
}
}
}
if (aLi == 1||count1==0) {
if (i == 0 && !matches(".*\\(.*", asp.get(0))) {
pdCountS++;
break;
}
if (count1 == 0) {
asp.set(i-1,asp.get(i-1)+"(");
}
else {
asp.set(i,asp.get(i)+"(");
}
break;
}
if (i == 0) {
pdCountS++;
}
}
for (int i = counti+1; i < asp.size(); i++) {
if (matches(".*\\).*", asp.get(i))){
for (int j = 0; j <asp.get(i).length() ; j++) {
if (asp.get(i).charAt(j)==')') {
countR++;
}
if (count2 == countR) {
aRi=1;
break;
}
}
}
if (aRi !=1 && matches(".*\\(.*", asp.get(i))){
for (int j = 0; j <asp.get(i).length() ; j++) {
if (asp.get(i).charAt(j)=='(') {
count2++;
}
}
}
if (aRi==1||count2==0) {
if (i+1 == asp.size() && !matches(".*\\).*", asp.get(i))) {
pdCountE++;
break;
}
if (count2 == 0) {
asp.set(i+1,")"+asp.get(i+1));
break;
}
else {
asp.set(i,")"+asp.get(i));
}
return asp;
}
}
return asp;
}
public static void main(String[] args) {
String a="5/(6-5)+(9.5+(9.6-4.2))";
System.out.println(run(a));
}
}