当前位置:   article > 正文

java 计算字符串表达式(支持个别数学函数,可修改添加)_calexp

calexp

1、Calculator

计算入口(calExp方法)

  1. import java.util.Collections;
  2. import java.util.Stack;
  3. /**
  4. * 算数表达式求值
  5. * 直接调用Calculator的类方法conversion()
  6. * 传入算数表达式,将返回一个浮点值结果
  7. * 如果计算过程错误,将返回一个NaN
  8. */
  9. public class Calculator {
  10. private Stack<String> postfixStack = new Stack<String>();// 后缀式栈
  11. private Stack<Character> opStack = new Stack<Character>();// 运算符栈
  12. private int[] operatPriority = new int[] { 0, 3, 2, 1, -1, 1, 0, 2 };// 运用运算符ASCII码-40做索引的运算符优先级
  13. /**
  14. * <b> 计算字符串式子(计算表达式)
  15. * </b><br><br><i>Description</i> :
  16. * @param expression String
  17. * @return double
  18. * <br><br>Date: 2020/1/4 13:31 <br>Author : dxl
  19. */
  20. public static double calExp(String expression) {
  21. double result = 0;
  22. Calculator cal = new Calculator();
  23. try {
  24. //DXL先进行特殊函数运算
  25. expression = FunHelper.subCalculate(expression);
  26. expression = transform(expression);
  27. result = cal.calculate(expression);
  28. } catch (Exception e) {
  29. // e.printStackTrace();
  30. // 运算错误返回NaN
  31. return 0.0 / 0.0;
  32. }
  33. // return new String().valueOf(result);
  34. return result;
  35. }
  36. /**
  37. * 将表达式中负数的符号更改
  38. *
  39. * @param expression
  40. * 例如-2+-1*(-3E-2)-(-1) 被转为 ~2+~1*(~3E~2)-(~1)
  41. * @return
  42. */
  43. private static String transform(String expression) {
  44. //DXL
  45. //好像是处理最前面负号(最开始做的,忘了)
  46. /
  47. for (int i = 0; i < expression.length(); i++){
  48. if (expression.charAt(i) == '-'){
  49. if (i == 0){
  50. expression = "0" + expression;
  51. }else{
  52. char c = expression.charAt(i-1);
  53. if (c == '(' ){
  54. expression = expression.substring(0, i) + "0" + expression.substring(i, expression.length());
  55. }
  56. }
  57. }
  58. }
  59. char[] arr = expression.toCharArray();
  60. for (int i = 0; i < arr.length; i++) {
  61. if (arr[i] == '-') {
  62. if (i == 0) {
  63. arr[i] = '~';
  64. } else {
  65. char c = arr[i - 1];
  66. if (c == '+' || c == '-' || c == '*' || c == '/' || c == '(' || c == 'E' || c == 'e') {
  67. arr[i] = '~';
  68. }
  69. }
  70. }
  71. }
  72. // if(arr[0]=='~'||arr[1]=='('){
  73. if(arr[0]=='~'){
  74. arr[0]='-';
  75. return "0"+new String(arr);
  76. }else{
  77. return new String(arr);
  78. }
  79. }
  80. /**
  81. * 按照给定的表达式计算
  82. *
  83. * @param expression
  84. * 要计算的表达式例如:5+12*(3+5)/7
  85. * @return
  86. */
  87. public double calculate(String expression) {
  88. Stack<String> resultStack = new Stack<String>();
  89. prepare(expression);
  90. Collections.reverse(postfixStack);// 将后缀式栈反转
  91. String firstValue, secondValue, currentValue;// 参与计算的第一个值,第二个值和算术运算符
  92. while (!postfixStack.isEmpty()) {
  93. currentValue = postfixStack.pop();
  94. if (!isOperator(currentValue.charAt(0))) {// 如果不是运算符则存入操作数栈中
  95. currentValue = currentValue.replace("~", "-");
  96. resultStack.push(currentValue);
  97. } else {// 如果是运算符则从操作数栈中取两个值和该数值一起参与运算
  98. secondValue = resultStack.pop();
  99. firstValue = resultStack.pop();
  100. // 将负数标记符改为负号
  101. firstValue = firstValue.replace("~", "-");
  102. secondValue = secondValue.replace("~", "-");
  103. String tempResult = calculate(firstValue, secondValue, currentValue.charAt(0));
  104. resultStack.push(tempResult);
  105. }
  106. }
  107. // System.out.println("double��calculate�е�resultStack��"+Double.valueOf(resultStack.pop()));
  108. return Double.valueOf(resultStack.pop());
  109. }
  110. /**
  111. * 数据准备阶段将表达式转换成为后缀式栈
  112. *
  113. * @param expression
  114. */
  115. private void prepare(String expression) {
  116. opStack.push(',');// 运算符放入栈底元素逗号,此符号优先级最低
  117. char[] arr = expression.toCharArray();
  118. int currentIndex = 0;// 当前字符的位置
  119. int count = 0;// 上次算术运算符到本次算术运算符的字符的长度便于或者之间的数值
  120. char currentOp, peekOp;// 当前操作符和栈顶操作符
  121. for (int i = 0; i < arr.length; i++) {
  122. currentOp = arr[i];
  123. if (isOperator(currentOp)) {// 如果当前字符是运算符
  124. if (count > 0) {
  125. postfixStack.push(new String(arr, currentIndex, count));// 取两个运算符之间的数字
  126. }
  127. peekOp = opStack.peek();
  128. if (currentOp == ')') {// 遇到反括号则将运算符栈中的元素移除到后缀式栈中直到遇到左括号
  129. while (opStack.peek() != '(') {
  130. postfixStack.push(String.valueOf(opStack.pop()));
  131. }
  132. opStack.pop();
  133. } else {
  134. while (currentOp != '(' && peekOp != ',' && compare(currentOp, peekOp)) {
  135. postfixStack.push(String.valueOf(opStack.pop()));
  136. peekOp = opStack.peek();
  137. }
  138. opStack.push(currentOp);
  139. }
  140. count = 0;
  141. currentIndex = i + 1;
  142. } else {
  143. count++;
  144. }
  145. }
  146. if (count > 1 || (count == 1 && !isOperator(arr[currentIndex]))) {// 最后一个字符不是括号或者其他运算符的则加入后缀式栈中
  147. postfixStack.push(new String(arr, currentIndex, count));
  148. }
  149. while (opStack.peek() != ',') {
  150. postfixStack.push(String.valueOf(opStack.pop()));// 将操作符栈中的剩余的元素添加到后缀式栈中
  151. }
  152. }
  153. /**
  154. * 判断是否为算术符号
  155. *
  156. * @param c
  157. * @return
  158. */
  159. private boolean isOperator(char c) {
  160. return c == '+' || c == '-' || c == '*' || c == '/' || c == '(' || c == ')';
  161. }
  162. /**
  163. * 利用ASCII码-40做下标去算术符号优先级
  164. *
  165. * @param cur
  166. * @param peek
  167. * @return
  168. */
  169. public boolean compare(char cur, char peek) {// 如果是peek优先级高于cur,返回true,默认都是peek优先级要低
  170. boolean result = false;
  171. if (operatPriority[(peek) - 40] >= operatPriority[(cur) - 40]) {
  172. result = true;
  173. }
  174. return result;
  175. }
  176. /**
  177. * 按照给定的算术运算符做计算
  178. *
  179. * @param firstValue
  180. * @param secondValue
  181. * @param currentOp
  182. * @return
  183. */
  184. private String calculate(String firstValue, String secondValue, char currentOp) {
  185. String result = "";
  186. switch (currentOp) {
  187. case '+':
  188. result = String.valueOf(ArithHelper.add(firstValue, secondValue));
  189. break;
  190. case '-':
  191. result = String.valueOf(ArithHelper.sub(firstValue, secondValue));
  192. break;
  193. case '*':
  194. result = String.valueOf(ArithHelper.mul(firstValue, secondValue));
  195. break;
  196. case '/':
  197. result = String.valueOf(ArithHelper.div(firstValue, secondValue));
  198. break;
  199. }
  200. return result;
  201. }
  202. /
  203. public static void main(String[] args) {
  204. // String expression = "2+3+sin(30+20-20+1-sin(45+45))";
  205. // String expression = "2+++++3";
  206. // String expression = "sin(0.0/0.0)";
  207. // String expression = "arcsin((20)/2/16)";
  208. String expression = "2+3+sin(13-tan(90/2)+sin(44+cos(50-20-30)+60-30/2))+tan(45)+2e1*arcsin(1/2.5)";
  209. // String expression = "(((sin(20+-10))))";
  210. // String expression = "5+(((-(((2+2)))/2)))";
  211. // String expression = "5+(-(((2+2)))/2)";
  212. // String expression = "5+(-(((2+2)))/2)";
  213. // String expression = "5+(-(2+2)/2)";
  214. double result = Calculator.calExp(expression);
  215. System.out.println(expression + " = " + result);
  216. System.out.println();
  217. }
  218. }

2、ArithHelper

使用java.math.BigDecimal计算double,保证double的计算精度

  1. public class ArithHelper {
  2. // 默认除法运算精度
  3. private static final int DEF_DIV_SCALE = 16;
  4. // 这个类不能实例化
  5. private ArithHelper() {
  6. }
  7. /**
  8. * 提供精确的加法运算。
  9. *
  10. * @param v1 被加数
  11. * @param v2 加数
  12. * @return 两个参数的和
  13. */
  14. public static double add(double v1, double v2) {
  15. java.math.BigDecimal b1 = new java.math.BigDecimal(Double.toString(v1));
  16. java.math.BigDecimal b2 = new java.math.BigDecimal(Double.toString(v2));
  17. return b1.add(b2).doubleValue();
  18. }
  19. public static double add(String v1, String v2) {
  20. java.math.BigDecimal b1 = new java.math.BigDecimal(v1);
  21. java.math.BigDecimal b2 = new java.math.BigDecimal(v2);
  22. return b1.add(b2).doubleValue();
  23. }
  24. /**
  25. * 提供精确的减法运算。
  26. *
  27. * @param v1 被减数
  28. * @param v2 减数
  29. * @return 两个参数的差
  30. */
  31. public static double sub(double v1, double v2) {
  32. java.math.BigDecimal b1 = new java.math.BigDecimal(Double.toString(v1));
  33. java.math.BigDecimal b2 = new java.math.BigDecimal(Double.toString(v2));
  34. return b1.subtract(b2).doubleValue();
  35. }
  36. public static double sub(String v1, String v2) {
  37. java.math.BigDecimal b1 = new java.math.BigDecimal(v1);
  38. java.math.BigDecimal b2 = new java.math.BigDecimal(v2);
  39. return b1.subtract(b2).doubleValue();
  40. }
  41. /**
  42. * 提供精确的乘法运算。
  43. *
  44. * @param v1
  45. * 被乘数
  46. * @param v2
  47. * 乘数
  48. * @return 两个参数的积
  49. */
  50. public static double mul(double v1, double v2) {
  51. java.math.BigDecimal b1 = new java.math.BigDecimal(Double.toString(v1));
  52. java.math.BigDecimal b2 = new java.math.BigDecimal(Double.toString(v2));
  53. return b1.multiply(b2).doubleValue();
  54. }
  55. public static double mul(String v1, String v2) {
  56. java.math.BigDecimal b1 = new java.math.BigDecimal(v1);
  57. java.math.BigDecimal b2 = new java.math.BigDecimal(v2);
  58. return b1.multiply(b2).doubleValue();
  59. }
  60. /**
  61. * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到 小数点以后10位,以后的数字四舍五入。
  62. *
  63. * @param v1
  64. * 被除数
  65. * @param v2
  66. * 除数
  67. * @return 两个参数的商
  68. */
  69. public static double div(double v1, double v2) {
  70. return div(v1, v2, DEF_DIV_SCALE);
  71. }
  72. public static double div(String v1, String v2) {
  73. java.math.BigDecimal b1 = new java.math.BigDecimal(v1);
  74. java.math.BigDecimal b2 = new java.math.BigDecimal(v2);
  75. return b1.divide(b2, DEF_DIV_SCALE, java.math.BigDecimal.ROUND_HALF_UP).doubleValue();
  76. }
  77. /**
  78. * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指 定精度,以后的数字四舍五入。
  79. *
  80. * @param v1 被除数
  81. * @param v2 除数
  82. * @param scale 表示表示需要精确到小数点以后几位。
  83. * @return 两个参数的商
  84. */
  85. public static double div(double v1, double v2, int scale) {
  86. if (scale < 0) {
  87. throw new IllegalArgumentException("The scale must be a positive integer or zero");
  88. }
  89. java.math.BigDecimal b1 = new java.math.BigDecimal(Double.toString(v1));
  90. java.math.BigDecimal b2 = new java.math.BigDecimal(Double.toString(v2));
  91. return b1.divide(b2, scale, java.math.BigDecimal.ROUND_HALF_UP).doubleValue();
  92. }
  93. /**
  94. * 提供精确的小数位四舍五入处理。
  95. *
  96. * @param v 需要四舍五入的数字
  97. * @param scale 小数点后保留几位
  98. * @return 四舍五入后的结果
  99. */
  100. public static double round(double v, int scale) {
  101. if (scale < 0) {
  102. throw new IllegalArgumentException("The scale must be a positive integer or zero");
  103. }
  104. java.math.BigDecimal b = new java.math.BigDecimal(Double.toString(v));
  105. java.math.BigDecimal one = new java.math.BigDecimal("1");
  106. return b.divide(one, scale, java.math.BigDecimal.ROUND_HALF_UP).doubleValue();
  107. }
  108. public static double round(String v, int scale) {
  109. if (scale < 0) {
  110. throw new IllegalArgumentException("The scale must be a positive integer or zero");
  111. }
  112. java.math.BigDecimal b = new java.math.BigDecimal(v);
  113. java.math.BigDecimal one = new java.math.BigDecimal("1");
  114. return b.divide(one, scale, java.math.BigDecimal.ROUND_HALF_UP).doubleValue();
  115. }
  116. }

3、CalHelper

做一些计算前处理,如转换、优先级比较

  1. import java.util.ArrayList;
  2. import java.util.Stack;
  3. public class CalHelper {
  4. /**
  5. * 将字符串转化成List
  6. * @param str
  7. * @return
  8. */
  9. public ArrayList<String> getStringList(String str){
  10. ArrayList<String> result = new ArrayList<String>();
  11. String num = "";
  12. for (int i = 0; i < str.length(); i++) {
  13. if(Character.isDigit(str.charAt(i))){
  14. num = num + str.charAt(i);
  15. }else{
  16. if(num != ""){
  17. result.add(num);
  18. }
  19. result.add(str.charAt(i) + "");
  20. num = "";
  21. }
  22. }
  23. if(num != ""){
  24. result.add(num);
  25. }
  26. return result;
  27. }
  28. /**
  29. * 将中缀表达式转化为后缀表达式
  30. * @param inOrderList
  31. * @return
  32. */
  33. //result = [12, +, (, 23, *, 3, -, 56, +, 7, ), *, (, 2, +, 90, ), /, 2]
  34. public ArrayList<String> getPostOrder(ArrayList<String> inOrderList){
  35. ArrayList<String> result = new ArrayList<String>();
  36. Stack<String> stack = new Stack<String>();
  37. for (int i = 0; i < inOrderList.size(); i++) {
  38. if(Character.isDigit(inOrderList.get(i).charAt(0))){
  39. result.add(inOrderList.get(i));
  40. }else{
  41. switch (inOrderList.get(i).charAt(0)) {
  42. case '(':
  43. stack.push(inOrderList.get(i));
  44. break;
  45. case ')':
  46. while (!stack.peek().equals("(")) {
  47. result.add(stack.pop());
  48. }
  49. stack.pop();
  50. break;
  51. default:
  52. while (!stack.isEmpty() && compare(stack.peek(), inOrderList.get(i))){
  53. result.add(stack.pop());
  54. }
  55. stack.push(inOrderList.get(i));
  56. break;
  57. }
  58. }
  59. }
  60. while(!stack.isEmpty()){
  61. result.add(stack.pop());
  62. }
  63. return result;
  64. }
  65. /**
  66. * 计算后缀表达式
  67. * @param postOrder
  68. * @return
  69. */
  70. //[12, 23, 3, *, 56, -, 7, +, 2, 90, +, *, 2, /, +]result
  71. @SuppressWarnings({ "unchecked", "rawtypes" })
  72. public Double calculate(ArrayList<String> postOrder){
  73. Stack stack = new Stack();
  74. for (int i = 0; i < postOrder.size(); i++) {
  75. if(Character.isDigit(postOrder.get(i).charAt(0))){
  76. stack.push(Double.parseDouble(postOrder.get(i)));
  77. }else{
  78. Double back = (Double)stack.pop();
  79. Double front = (Double)stack.pop();
  80. Double res = 0.0;
  81. switch (postOrder.get(i).charAt(0)) {
  82. case '+':
  83. res = front + back;
  84. break;
  85. case '-':
  86. res = front - back;
  87. break;
  88. case '*':
  89. res = front * back;
  90. break;
  91. case '/':
  92. res = front / back;
  93. break;
  94. }
  95. stack.push(res);
  96. }
  97. }
  98. return (Double)stack.pop();
  99. }
  100. /**
  101. * 比较运算符等级
  102. * @param peek
  103. * @param cur
  104. * @return
  105. */
  106. public static boolean compare(String peek, String cur){
  107. if("*".equals(peek) && ("/".equals(cur) || "*".equals(cur) ||"+".equals(cur) ||"-".equals(cur))){
  108. return true;
  109. }else if("/".equals(peek) && ("/".equals(cur) || "*".equals(cur) ||"+".equals(cur) ||"-".equals(cur))){
  110. return true;
  111. }else if("+".equals(peek) && ("+".equals(cur) || "-".equals(cur))){
  112. return true;
  113. }else if("-".equals(peek) && ("+".equals(cur) || "-".equals(cur))){
  114. return true;
  115. }
  116. return false;
  117. }
  118. }

4、FunHelper

处理表达式中数学函数的计算,根据需求可在(funCalculate)这里面添加

  1. public class FunHelper {
  2. /**
  3. * 应该是支持函数运算(以前做的,注释忘了)
  4. * @param funname
  5. * @param substring
  6. * @return String substring
  7. */
  8. public static String funCalculate (String funname,String substring){
  9. double subvalue = Calculator.calExp(substring);
  10. if (Double.isNaN(subvalue)){
  11. System.out.println("计算"+substring+"出错 :(");
  12. substring = "+++";
  13. return substring;
  14. }
  15. switch (funname) {
  16. case "arcsin":
  17. subvalue = Math.asin(subvalue)*180/Math.PI;
  18. substring = String.valueOf(subvalue);
  19. break;
  20. case "arccos":
  21. subvalue = Math.acos(subvalue)*180/Math.PI;
  22. substring = String.valueOf(subvalue);
  23. break;
  24. case "arctan":
  25. subvalue = Math.atan(subvalue)*180/Math.PI;
  26. break;
  27. case "sin":
  28. subvalue = Math.sin(Math.PI*subvalue/180);
  29. substring = String.valueOf(subvalue);
  30. break;
  31. case "cos":
  32. subvalue = Math.cos(Math.PI*subvalue/180);
  33. substring = String.valueOf(subvalue);
  34. break;
  35. case "tan":
  36. subvalue = Math.tan(Math.PI*subvalue/180);
  37. substring = String.valueOf(subvalue);
  38. break;
  39. default : System.out.println("不支持"+funname+"() :(");
  40. substring = "+++";
  41. break;
  42. }
  43. return substring;
  44. }
  45. ///
  46. public static String subCalculate(String expression){
  47. for (int i = 0; i < expression.length(); i++) {
  48. char temchar1 = expression.charAt(i);
  49. Integer tem1 = Integer.valueOf(temchar1);
  50. if(tem1 >96 && tem1 < 123 && tem1 != 101){
  51. int indexleft = i;
  52. int indexright = i;
  53. int p = 0;
  54. for (int j = i;j < expression.length(); j++){
  55. if (expression.charAt(j) == '(' ){
  56. indexleft = j;
  57. break;
  58. }
  59. }
  60. for (int t = indexleft+1;t < expression.length(); t++){
  61. if (expression.charAt(t) == '('){
  62. p++;
  63. }else{
  64. if (expression.charAt(t) == ')'){
  65. p--;
  66. }
  67. }
  68. if (p == -1){
  69. indexright = t;
  70. break;
  71. }
  72. }
  73. String substring = expression.substring(indexleft+1, indexright);
  74. String subvalue = FunHelper.funCalculate(expression.substring(i, indexleft),substring);
  75. String funstring = expression.substring(i, indexright+1);
  76. expression = expression.replace(funstring,subvalue);
  77. }
  78. }
  79. return expression;
  80. }
  81. }
PS:很早指点优化的,现在重新发布下,自行修改
 
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/319781
推荐阅读
相关标签
  

闽ICP备14008679号