02 计算字符串表达式 [类似于js eval函数]

原创 2015年11月21日 15:43:08

前言

这个 也是之前的时候在贴吧里看到的一个小题目, 无聊的时候引起了兴趣, 于是 当时就做了做, 但是 当时思考的时候, 设计的太细节化了[对于()之间的表达式, 现在 我绝对是递归计算, 而并非将其提取出来单独计算], 所以 导致存在很多错误, 但是 后来再来看这个的时候, 发现 居然有四百多行, 顿时就没了改正的心情了
原帖 : http://tieba.baidu.com/p/3787837745

/**
 * file name : Test03EvalLike.java
 * created at : 8:53:59 AM May 14, 2015
 * created by 970655147
 */

最近 闲来无事看看以前的代码的时候, 突然想起了, 就将这个问题重新思考了一下

思路如下

对于 一个字符串的表达式, 如果需要计算他的话, 首先需要解析其中的操作符, 以及操作数
这里写图片描述

1 首先 需要解析各个符号
对于 “+-*/%” 获取该符号 以及位置
对于”()”递归eval”(“, “)”之间的表达式, 存储起来

2 业务处理
2.1) 初始值的处理
如果lastOptNow为null
    判断第一个符号是否是”(“, 如果是则获取第一个()之间的结果, 作为初始值[入口 : 1. eval, 2. 下一个符号为”(” 并且下下一个符号的优先级比当前运算符的优先级高]
        否则 解析第一个符号左边的数据作为操作数[入口 : 1. eval]
    否则 如果给定了lastOptNow 解析lastOptNow到第一个符号之间的数据作为初始值 [入口 : 1. 下一个操作符的优先级比当前操作符优先级高, 并且下一个操作符不为”(” ]

2.2) 计算表达式[计算最后一个操作符之前的表达式]
获取下一个操作符, 以及下下一个操作符
如果下一个操作符的优先级比下下一个操作符的优先级高
    如果下一个操作符为”(“,
        如果当前”(“的对应的下一个”)”之后的下一个操作符的优先级比当前操作符的优先级高 计算当前结果和剩下的其他表达式的结果, 返回结果
            否则 计算当前结果 和()之间的计算结果进行计算, 进入下一个循环
        否则 计算当前结果和剩下的其他表达式的结果, 返回结果
    否则 更新当前结果为当前结果和下一个操作数之间的计算结果

2.3) 如果剩余的操作符列表大于1个操作符 计算最后一个操作符

2.4) 返回结果

参考代码

/**
 * file name : Test07Eval.java
 * created at : 5:12:16 PM Nov 20, 2015
 * created by 970655147
 */

package com.hx.test08;

public class Test07EvalLike {

    // main
    public static void main(String []args) {

        // 1. 简单的测试
//      String exp = "2 * ( 3 + 4 )";
//      String exp = "2 * ( 3 + 4 ) + (3 * 5)";
//      String exp = "3 * 2 * ( 3 + 4 )";
//      String exp = "3 + 3 * 2 * ( 3 + 4 ) + 2";
        String exp = "3 + 3 * 2 * ( 3 + (3 * 6) + 4 ) + 2";
//      String exp = "3 * 80 + 81 * 93";
//      String exp = "3 + 3 * 2 * ( 3 + 4 )";
//      String exp = "(3 * 2) * ( 3 + 4 )";
        // ...
//      String exp = "91 * 32 - 8 - 2";

        int val = eval(exp);

        System.out.println(val);


        // 2. 多次随机测试
//      randomTest();

    }

    // 各个符号的常量, 匹配的符号映射, 各个符号的优先级
    private final static char ADD = '+';
    private final static char SUB = '-';
    private final static char MUL = '*';
    private final static char DIV = '/';
    private final static char MOD = '%';
    private final static char LEFT_BRACKET = '(';
    private final static char RIGHT_BRACKET = ')';
    private final static Map<Character, Character> matched = new HashMap<>();
    private static Map<Character, Integer> priority = new HashMap<>();

    // 初始化
    static {
        matched.put(LEFT_BRACKET, RIGHT_BRACKET);

        priority.put(ADD, 0);
        priority.put(SUB, 0);
        priority.put(MUL, 1);
        priority.put(DIV, 1);
        priority.put(MOD, 1);
        priority.put(LEFT_BRACKET, 2);
        priority.put(RIGHT_BRACKET, 2);
    }

    // 先统计出exp中各个符号的位置  在用eval0真正计算
    // 首先记录所有的符号的位置, 添加到一个符号列表中[如果 有子计算过程, 递归计算]
    // 然后  计算给定的表达式
    public static int eval(String exp) {
        // Operator, Operation
        List<Integer> resInBrackets = new LinkedList<>();
        List<Operator> optr = new LinkedList<Operator>();
        for(int i=0; i<exp.length(); i++) {
            char c = exp.charAt(i);
            switch (c ) {
                case ADD :
                case SUB :
                case MUL :
                case DIV :
                case MOD :
                    optr.add(new Operator(c, i));
                    break;
                case LEFT_BRACKET :
                    int nextI = getNextMatched(exp, i+1, LEFT_BRACKET);
                    resInBrackets.add(eval(exp.substring(i+1, nextI)) );
                    optr.add(new Operator(LEFT_BRACKET, i) );
                    optr.add(new Operator(RIGHT_BRACKET, nextI) );
                    i = nextI;
                default :
                    break;
            }
        }

//      while(optr.size() > 0) {
//          System.out.println(optr.pop());
//      }

        int val = eval0(exp, optr, resInBrackets, null);


        return val;
    }

    // 计算给定的表达式  从首 -> 尾
    // 获取第一个操作数作为默认的返回值
        // 然后 获取第一个操作符, 以及下一个操作符
        // 如果下一个操作符的优先级大于当前操作符, 递归计算(当前结果, 第一个操作符之后的部分) [这里可以为本方法添加两个参数, 将带return的递归构造成尾递归, 不过这里  就不做了]
            // 否则  计算第一个操作符旁边的两个操作数, 循环, 直到操作符列表中只剩下一个操作符
    // 计算最后一个操作符  二边的操作数
    private static int eval0(String exp, List<Operator> optr, List<Integer> resInBrackets, Operator lastOptNow) {
        int res = 0;
        if(lastOptNow == null) {
            Operator firstOpt = optr.get(0);
            if(firstOpt.oper == LEFT_BRACKET) {
                optr.remove(0);
                optr.remove(0);
                res = resInBrackets.remove(0);
            } else {
                res = parseFirstInt(exp, firstOpt );
            }
        } else {
            res = parseInt(exp, lastOptNow, optr.get(0) );
        }

        while(optr.size() > 1) {
            Operator optNow = optr.get(0);
            Operator optNext = optr.get(1);
            optr.remove(0);
            if(priority.get(optNext.oper) > priority.get(optNow.oper) ) {
                if(optNext.oper == LEFT_BRACKET) {
                    Operator optNNext = null;
                    if(optr.size() > 2) {
                        optNNext = optr.get(2);
                    }
                    if((optNNext != null) && (priority.get(optNNext.oper) > priority.get(optNow.oper)) ) {
                        return calc(res, optNow, eval0(exp, optr, resInBrackets, null) );
                    } else {
                        optr.remove(0);
                        optr.remove(0);
                        res = calc(res, optNow, resInBrackets.remove(0) );
                    }
                } else {
                    return calc(res, optNow, eval0(exp, optr, resInBrackets, optNow) );
                }
            } else {
                res = calc(res, optNow, parseInt(exp, optNow, optNext) );
            }
        }

        if(optr.size() > 0) {
            res = calc(res, optr.get(0), parseLastInt(exp, optr.get(0)) );
        }

        return res;
    }

    // 计算val01, val02  的opt操作  返回结果
    private static int calc(int val01, Operator opt, int val02) {   
        int val = 0;
        switch(opt.oper) {
            case ADD :
                val = val01 + val02;
                break;
            case SUB :
                val = val01 - val02;
                break;
            case MUL :
                val = val01 * val02;
                break;
            case DIV :
                val = val01 / val02;
                break;
            case MOD :
                val = val01 % val02;
                break;
        }

        return val;
    }

    // 解析当前云算符 和下一个云算法之间的的操作数
    private static int parseInt(String exp, Operator optNow, Operator optNext) {
        String intStr = null;
        try {
            intStr = exp.substring(optNow.index+1, optNext.index).trim();
            return Integer.parseInt(intStr);
        } catch (Exception e) {
            Log.err("error while parse first operand[idx : " + optNow.index + "][' " + intStr + " '] !");
            throw e;
        }
    }
    private static int parseLastInt(String exp, Operator optNow) {
        try {
            return Integer.parseInt(exp.substring(optNow.index+1).trim() );
        } catch(Exception e) {
            Log.err("error while parse last operand[idx : " + optNow.index + "] !");
            throw e;
        }
    }
    private static int parseFirstInt(String exp, Operator optNow) {
        try {
            return Integer.parseInt(exp.substring(0, optNow.index).trim() );
        } catch(Exception e) {
            Log.err("error while parse first operand[idx : " + optNow.index + "] !");
            throw e;
        }
    }

    // 获取当前位置的符号匹配的下一个符号
    private static int getNextMatched(String exp, int idx, char left) {
        Deque<Character> stack = new LinkedList<>();
        stack.push(left);
        for(int i=idx; i<exp.length(); i++) {
            char ch = exp.charAt(i);
            if(matched.containsKey(ch) ) {
                stack.push(ch);
            }
            if(ch == matched.get(stack.peek()) ) {
                stack.pop();
                if(stack.size() == 0) {
                    idx = i;
                    break ;
                }
            }
        }

        return idx;
    }

    // --------------- bean --------------------

    // 封装每一个操作符   以及其索引
    static class Operator {
        // 操作符, 索引
        char oper;
        int index;

        // 初始化
        public Operator() {

        }
        public Operator(char oper, int index) {
            this.oper = oper;
            this.index = index;
        }

        // for debug ..
        public String toString() {
            return oper + " -> " + index + "; ";
        }
    }

}

效果截图
这里写图片描述

这里写图片描述

版权声明:本文为博主原创文章,未经博主允许不得转载。

JavaScript表达式计算 eval

JavaScript可以解释运行由JavaScript源代码组成的字符串,并产生一个值。JavaScript通过全局函数eval()来完成这个工作。 举例: eval("3+2")   //=> ...
  • Winterto1990
  • Winterto1990
  • 2015年10月05日 11:30
  • 1595

JavaScript实现计算复杂计算公式(支持括号)

//计算器 var CalcEval = function() {}; //复杂的计算方法(包含括号) CalcEval.prototype.complexEval = function...
  • u010973529
  • u010973529
  • 2015年08月07日 14:49
  • 1358

js简单的计算器(使用eval)

window.onload = function () { var result = document.getElementById("txtResult"); //获取值得...
  • zhao8912
  • zhao8912
  • 2014年10月22日 21:40
  • 1575

js 运算误差解决方案

1.因为计算机只认识二进制,所以某些数字二进制是无限循环的,例如:0.1=> 0.0001 1001 1001 ...无限循环   ,所以产生了精度问题,c这类语言已经封装好方法来避免,然而js并没有...
  • maoguiyou
  • maoguiyou
  • 2015年11月26日 16:33
  • 2069

java执行字符串数学表达式 ScriptEngine

今天遇到了一个需要将数据库中保存的表达式,替换其中的字符,并计算出值,java是不能直接计算的例如:  Java代码   double d = (3+5-(2-4)*2...
  • w1014074794
  • w1014074794
  • 2015年05月25日 13:25
  • 2338

python算法——字符串表达式的计算

preface:最近有个面试,被要求给出一个字符串表达式,计算出结果。本以为是见到过的,想着用一个栈,然后被面试官打断说你这样是有问题的,然后想了说用树,又被打断说是有问题的,再仔细想想。结果还是没整...
  • u010454729
  • u010454729
  • 2016年07月10日 18:43
  • 3713

使用Python运算一个字符串表达式

如何运行一个表达式,例如:12+23*4/2这个我想大家都很了解。不过,如果这个表达式是一个字符串呢?或是这样来描述,一个表达式被写成了一个字符串,我们又应该如何去运行并求得值呢?...
  • u013761665
  • u013761665
  • 2015年04月09日 21:20
  • 3598

c#中实现类似js的Eval|.NET中执行Javascript(表达式是字符串的计算)

我们一般只知道javascript中有个eval方法,其实在c# 中也有这样一个方法,下面我们讲解c#中Eval的使用方法。   第一步在你的项目中添加Microsoft.Vsa和Microsoft....
  • liujun198773
  • liujun198773
  • 2011年05月12日 14:00
  • 1741

Java 工具类, 计算字符串表达式, 支持 +,-,*,/,%. ()

  • 2013年05月10日 16:26
  • 10KB
  • 下载

字符串表达式解释计算动态连接库

  • 2009年05月06日 18:39
  • 1.62MB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:02 计算字符串表达式 [类似于js eval函数]
举报原因:
原因补充:

(最多只允许输入30个字)