通过math.js+正则表达式实现一个自定义的math.eval方法,对字符串表达式进行求值运算

276 阅读1分钟

math.js文档地址:mathjs.org/docs/index.…

import * as math from 'mathjs'
class mathPlugin {
  static install(Vue) {
    Vue.prototype.$math={}
     /**
     * @param string expression 表达式
     * @param number length 返回的小数位
     * @param boolean roundUp 是否四舍五入,默认四舍五入
     * @returns 
     */
     Vue.prototype.$math._eval = (expression,length,roundUp) => {
        length = length || 0
        roundUp = roundUp || false;
        const regex = /(\d+(\.\d+)?|\+|\-|\*|\/|\(|\))/g;
        let str = removeSpaces(expression); // 调用 removeSpaces 方法去掉空格
        const matchs = str.match(regex);
        const stack = [];
        const output = [];
        const operatorPriority = {
            '+': 1,
            '-': 1,
            '*': 2,
            '/': 2,
        };
        for (let i = 0; i < matchs.length; i++) {
            const token = matchs[i];
            if (token === '+' || token === '-' || token === '*' || token === '/') {
                while (stack.length > 0 && operatorPriority[token] <= operatorPriority[stack[stack.length - 1]]) {
                    output.push(stack.pop());
                }
                stack.push(token);
            } else if (token === '(') {
                stack.push(token);
            } else if (token === ')') {
                while (stack.length > 0 && stack[stack.length - 1] !== '(') {
                    output.push(stack.pop());
                }
                stack.pop();
            } else {
                output.push(token);
            }
        }

        while (stack.length > 0) {
            output.push(stack.pop());
        }

        for (let i = 0; i < output.length; i++) {
            const token = output[i];
            if (token === '+' || token === '-' || token === '*' || token === '/') {
                const num2 = stack.pop();
                const num1 = stack.pop();
                if (token === '+') {
                    stack.push(math.add(math.bignumber(Number(num1)),math.bignumber(Number(num2))));
                } else if (token === '-') {
                    stack.push(math.subtract(math.bignumber(Number(num1)),math.bignumber(Number(num2))));
                } else if (token === '*') {
                    stack.push(math.multiply(math.bignumber(Number(num1)),math.bignumber(Number(num2))));
                } else if (token === '/') {
                    stack.push(math.divide(math.bignumber(Number(num1)),math.bignumber(Number(num2))));
                }
            } else {
                stack.push(token);
            }
        }
        if(length==0) return stack[0].toString();
        return roundUp ? formatRound(stack[0].toString(),length) : formatNumber(stack[0].toString(),length);
    }
    
    /**
     * toFixed是银行家舍入规则:
     * 当要舍弃的数字是5时,如果5后面还有非零数字,则应该向上舍入;
     * 如果5后面没有数字,或者后面都是0,则需要根据5前面的数字来判断
     * 如果5前面的数字是偶数,则应该向下舍入;
     * 如果5前面的数字是奇数,则应该向上舍入;
     * @param number decimalPlaces 小数位数
     * @param boolean roundUp  是否四舍五入,默认四舍五入
     * @returns 
     */
    function ntoFixed (argNum,length,roundUp) {
        roundUp = roundUp || false;
        var num = parseFloat(argNum);    
        if (isNaN(num)) {    
            return 0;    
        }
        var factor = Math.pow(10, length);
        var truncated = 0;
        if(roundUp){
            truncated = Math.round(num * factor) / factor
        }else{
            truncated = Math.floor(num * factor) / factor
        }
        var str = truncated.toString();
        var index = str.indexOf(".");
        if (index === -1) {
            if(length > 0) str += "."; // 添加小数点
        }
        var decimalIndex = str.indexOf("."); 
        var zerosToAdd = 0;
        if(length!=0){
            zerosToAdd = length - (str.length - 1 - decimalIndex);
        }
        for (var i = 0; i <zerosToAdd; i++) {
            str += "0";
        }
        var diff = num - truncated;
        if (roundUp && diff >= 0.5 / factor) {
            str = (truncated + 1 / factor).toFixed(length);
        }
        return str;
    }
    //四舍五入
    function formatRound(num,length) {
        let str = ntoFixed(num,length); // 将数字保留指定位数小数
        let dotIndex = str.indexOf("."); // 查找小数点位置
        if (dotIndex === -1) { // 如果没有小数点
          if(length > 0) str += "."; // 添加小数点
          for (let i = 0; i < length; i++) { // 在末尾添加指定位数的0
            str += "0";
          }
        } else { // 如果有小数点
          let decimalPart = str.substr(dotIndex + 1); // 获取小数部分
          let diff = length - decimalPart.length; // 计算需要添加的0的个数
          for (let i = 0; i < diff; i++) { // 在小数部分末尾添加0
            str += "0";
          }
        }
        return str;
    }

    //字符串截取不四舍五入
    function formatNumber(num, length) {
        let str = num.toString(); // 将数字转换为字符串
        let dotIndex = str.indexOf("."); // 查找小数点位置
        if (dotIndex === -1) { // 如果没有小数点
            if(length > 0) str += "."; // 添加小数点
            for (let i = 0; i < length; i++) { // 在末尾添加指定位数的0
                str += "0";
            }
        } else { // 如果有小数点
            let decimalPart = str.substring(dotIndex + 1); // 获取小数部分
            let diff = length - decimalPart.length; // 计算需要添加的0的个数
            if (diff > 0) { // 如果需要添加0
                for (let i = 0; i < diff; i++) { // 在小数部分末尾添加0
                decimalPart += "0";
                }
                str = str.substring(0, dotIndex + 1) + decimalPart; // 拼接新的字符串
            } else { // 如果需要截取小数部分
                str = str.substring(0, dotIndex + length + 1); // 截取指定位数的小数部分
            }
        }
        return str;
    }

    function removeSpaces(str) {
        return str.replace(/\s+/g, '');
    }
  }
}
export default mathPlugin

推荐一个好用的正则表达式可视化网站 www.wangwl.net/static/proj…

todo:例外计算的的数字太大还是会变成科学计数法