JSUtils.java
14.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
package com.skua.tool.util;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 加减乘除表达式求值算法
* @version 1.0
*/
public class JSUtils {
public static String add(Object obj1 , Object obj2){
String expression = obj1+ "+" + obj2;
return executeExpression(expression,"0.00");
}
public static String subtract(Object obj1 , Object obj2){
String expression = obj1+ "-" + obj2;
return executeExpression(expression,"0.00");
}
public static String multiply(Object obj1 , Object obj2){
String expression = obj1+ "*" + obj2;
return executeExpression(expression,"0.00");
}
public static String divide(Object obj1 , Object obj2){
String expression = obj1+ "/" + obj2;
return executeExpression(expression,"0.00");
}
/***
* 格式化结果,保留两位小数
* @param obj1
* @return
*/
public static String format(Object obj1 ){
return format(obj1,null);
}
/***
* 格式化结果,指定格式,默认保留两位小数
* @param obj1
* @param decimalFormat
* @return
*/
public static String format(Object obj1 , String decimalFormat){
String result = null;
try{
if(obj1 != null ){
if(decimalFormat == null ) decimalFormat = "0.00";
DecimalFormat df = new DecimalFormat(decimalFormat);
result = df.format(Double.parseDouble(obj1.toString()));
}
}catch(Exception e){
e.printStackTrace();
}
return result;
}
/**
* 输入加减乘除表达式字符串,返回计算结果
*
* @param expression 表达式字符串
* @return 返回计算结果
*/
public static String executeExpression(String expression , String decimalFormat) {
String result = "0";
if(decimalFormat == null ) decimalFormat = "0.00";
try{
Double aDouble = executeExpression( expression );
DecimalFormat df = new DecimalFormat(decimalFormat);
result = df.format(aDouble);
}catch(Exception e ){
// e.printStackTrace();
}
return result;
}
// 表达式字符合法性校验正则模式,静态常量化可以降低每次使用都要编译地消耗
private static final Pattern EXPRESSION_PATTERN = Pattern.compile("[0-9\\.+-/*()= ]+");
// 运算符优先级map
private static final Map<String, Integer> OPT_PRIORITY_MAP = new HashMap<String, Integer>() {
private static final long serialVersionUID = 6968472606692771458L;
{
put("(", 0);
put("+", 2);
put("-", 2);
put("*", 3);
put("/", 3);
put(")", 7);
put("=", 20);
}
};
public static double executeExpression(String expression) {
// 非空校验
if (null == expression || "".equals(expression.trim())) {
throw new IllegalArgumentException("表达式不能为空!");
}
// 表达式字符合法性校验
Matcher matcher = EXPRESSION_PATTERN.matcher(expression);
if (!matcher.matches()) {
throw new IllegalArgumentException("表达式含有非法字符!");
}
Stack<String> optStack = new Stack<>(); // 运算符栈
Stack<BigDecimal> numStack = new Stack<>(); // 数值栈,数值以BigDecimal存储计算,避免精度计算问题
StringBuilder curNumBuilder = new StringBuilder(16); // 当前正在读取中的数值字符追加器
// 逐个读取字符,并根据运算符判断参与何种计算
for (int i = 0; i < expression.length(); i++) {
char c = expression.charAt(i);
if (c != ' ') { // 空白字符直接丢弃掉
if ((c >= '0' && c <= '9') || c == '.') {
curNumBuilder.append(c); // 持续读取一个数值的各个字符
} else {
if (curNumBuilder.length() > 0) {
// 如果追加器有值,说明之前读取的字符是数值,而且此时已经完整读取完一个数值
numStack.push(new BigDecimal(curNumBuilder.toString()));
curNumBuilder.delete(0, curNumBuilder.length());
}
String curOpt = String.valueOf(c);
if (optStack.empty()) {
// 运算符栈栈顶为空则直接入栈
optStack.push(curOpt);
} else {
if (curOpt.equals("(")) {
// 当前运算符为左括号,直接入运算符栈
optStack.push(curOpt);
} else if (curOpt.equals(")")) {
// 当前运算符为右括号,触发括号内的字表达式进行计算
directCalc(optStack, numStack, true);
} else if (curOpt.equals("=")) {
// 当前运算符为等号,触发整个表达式剩余计算,并返回总的计算结果
directCalc(optStack, numStack, false);
return numStack.pop().doubleValue();
} else {
// 当前运算符为加减乘除之一,要与栈顶运算符比较,判断是否要进行一次二元计算
compareAndCalc(optStack, numStack, curOpt);
}
}
}
}
}
// 表达式不是以等号结尾的场景
if (curNumBuilder.length() > 0) {
// 如果追加器有值,说明之前读取的字符是数值,而且此时已经完整读取完一个数值
numStack.push(new BigDecimal(curNumBuilder.toString()));
}
directCalc(optStack, numStack, false);
return numStack.pop().doubleValue();
}
/**
* 拿当前运算符和栈顶运算符对比,如果栈顶运算符优先级高于或同级于当前运算符,
* 则执行一次二元运算(递归比较并计算),否则当前运算符入栈
*
* @param optStack 运算符栈
* @param numStack 数值栈
* @param curOpt 当前运算符
*/
public static void compareAndCalc(Stack<String> optStack, Stack<BigDecimal> numStack,
String curOpt) {
// 比较当前运算符和栈顶运算符的优先级
String peekOpt = optStack.peek();
int priority = getPriority(peekOpt, curOpt);
if (priority == -1 || priority == 0) {
// 栈顶运算符优先级大或同级,触发一次二元运算
String opt = optStack.pop(); // 当前参与计算运算符
BigDecimal num2 = numStack.pop(); // 当前参与计算数值2
BigDecimal num1 = numStack.pop(); // 当前参与计算数值1
BigDecimal bigDecimal = floatingPointCalc(opt, num1, num2);
// 计算结果当做操作数入栈
numStack.push(bigDecimal);
// 运算完栈顶还有运算符,则还需要再次触发一次比较判断是否需要再次二元计算
if (optStack.empty()) {
optStack.push(curOpt);
} else {
compareAndCalc(optStack, numStack, curOpt);
}
} else {
// 当前运算符优先级高,则直接入栈
optStack.push(curOpt);
}
}
/**
* 遇到右括号和等号执行的连续计算操作(递归计算)
*
* @param optStack 运算符栈
* @param numStack 数值栈
* @param isBracket true表示为括号类型计算
*/
public static void directCalc(Stack<String> optStack, Stack<BigDecimal> numStack,
boolean isBracket) {
String opt = optStack.pop(); // 当前参与计算运算符
BigDecimal num2 = numStack.pop(); // 当前参与计算数值2
BigDecimal num1 = numStack.pop(); // 当前参与计算数值1
BigDecimal bigDecimal = floatingPointCalc(opt, num1, num2);
// 计算结果当做操作数入栈
numStack.push(bigDecimal);
if (isBracket) {
if ("(".equals(optStack.peek())) {
// 括号类型则遇左括号停止计算,同时将左括号从栈中移除
optStack.pop();
} else {
directCalc(optStack, numStack, isBracket);
}
} else {
if (!optStack.empty()) {
// 等号类型只要栈中还有运算符就继续计算
directCalc(optStack, numStack, isBracket);
}
}
}
/**
* 不丢失精度的二元运算,支持高精度计算
*
* @param opt
* @return
*/
public static BigDecimal floatingPointCalc(String opt, BigDecimal bigDecimal1,
BigDecimal bigDecimal2) {
BigDecimal resultBigDecimal = new BigDecimal(0);
switch (opt) {
case "+":
resultBigDecimal = bigDecimal1.add(bigDecimal2);
break;
case "-":
resultBigDecimal = bigDecimal1.subtract(bigDecimal2);
break;
case "*":
resultBigDecimal = bigDecimal1.multiply(bigDecimal2);
break;
case "/":
resultBigDecimal = bigDecimal1.divide(bigDecimal2, 10, BigDecimal.ROUND_HALF_DOWN); // 注意此处用法
break;
default:
break;
}
return resultBigDecimal;
}
/**
* priority = 0 表示两个运算符同级别
* priority = 1 第二个运算符级别高,负数则相反
*
* @param opt1
* @param opt2
* @return
*/
public static int getPriority(String opt1, String opt2) {
int priority = OPT_PRIORITY_MAP.get(opt2) - OPT_PRIORITY_MAP.get(opt1);
return priority;
}
/**
* 浮点数相等比较函数
*
* @param value1
* @param value2
* @return
*/
private static boolean isDoubleEquals(double value1, double value2) {
System.out.println("正确结果=" + value1 + ", 实际计算结果=" + value2);
return Math.abs(value1 - value2) <= 0.0001;
}
/* public static void main(String[] args) throws ScriptException {
Scanner in = new Scanner(System.in);
System.out.println("请输入一个算法表达式:");
String str = in.next();
// 表达式字符合法性校验
Matcher matcher = EXPRESSION_PATTERN.matcher(str);
if (!matcher.matches()) {
System.out.println("aaaaa");
return;
}
ScriptEngine se = new ScriptEngineManager().getEngineByName("JavaScript");
Object eval = se.eval(str);
Double aDouble = Double.valueOf(eval.toString());
BigDecimal b = new BigDecimal(aDouble);
double doubleRate = b.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
str +="=";
double aa = executeExpression(str);
System.out.println("js算:"+doubleRate+"---算法:"+aa);
// // 几个测试数据
// System.out.println(isDoubleEquals(-39.5, executeExpression(" 2 + 3/2 * 3 - 4 *(2 + 5 -2*4/2+9) + 3 + (2*1)-3= ")));
// System.out.println(isDoubleEquals(60.3666, executeExpression("9*2+1/3*2-4+(3*2/5+3+3*6)+3*5-1+3+(4+5*1/2)=")));
// System.out.println(isDoubleEquals(372.8, executeExpression(" 9.2 *(20-1)-1+199 = ")));
// System.out.println(isDoubleEquals(372.8, executeExpression(" 9.2 *(20-1)-1+199 ")));
// System.out.println(isDoubleEquals(372.8, executeExpression(" 9.2 *(20-1)-1+199")));
// System.out.println(isDoubleEquals(-29, executeExpression(" 9 *(20-1)-(1+199) ")));
// System.out.println(isDoubleEquals(1.0E24, executeExpression("1000000000000*1000000000000 = ")));
}*/
public static Boolean executeExpressionForBoolean(String expression){
ScriptEngine se = new ScriptEngineManager().getEngineByName("JavaScript");
Object eval = null;
Boolean result = null;
try {
eval = se.eval(expression);
result = Boolean.parseBoolean(eval.toString());
} catch (ScriptException e) {
//e.printStackTrace();
return null;
}
return result;
}
public static String executeExpressionForString(String expression){
ScriptEngine se = new ScriptEngineManager().getEngineByName("JavaScript");
Object eval = null;
String result = null;
try {
eval = se.eval(expression);
result = eval.toString();
} catch (ScriptException e) {
//e.printStackTrace();
return null;
}
return result;
}
/***
* 分割字符串,然后拼接
* @param str 带分隔字符串
* @param splitStr 分割符号
* @return
*/
public static String quoteEach(String str,String splitStr) {
if( str != null && str.length() > 0 ){
String[] items = str.split(splitStr);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < items.length; i++) {
if (i > 0) {
sb.append(", ");
}
sb.append('\'').append(items[i]).append('\'');
}
return sb.toString();
}
return str;
}
/* ScriptEngine se = new ScriptEngineManager().getEngineByName("JavaScript");
Object eval = null;
try {
eval = se.eval("1+2+(1*3)");
Double aDouble = Double.valueOf(eval.toString());
DecimalFormat df = new DecimalFormat("0.00");
String amountstr = df.format(aDouble);
//amount = new BigDecimal(amountstr);
System.out.println("amountstr = "+amountstr);
} catch (ScriptException e) {
e.printStackTrace();
}*/
}