Оценка строки как математического выражения в JavaScript



как разобрать и оценить математическое выражение в строке (например '1+1') без вызова eval(string) чтобы получить его числовое значение?



С этим примером я хочу, чтобы функция принимала '1+1' и возврат 2.

1057   14  

14 ответов:

можно использовать JavaScript Expression Evaluator library, что позволяет вам делать такие вещи, как:

Parser.evaluate("2 ^ x", { x: 3 });

или mathjs, который позволяет такие вещи, как:

math.eval('sin(45 deg) ^ 2');

в итоге я выбрал mathjs для одного из моих проектов.

кто-то должен разобрать эту строку. Если это не интерпретатор (via eval) тогда вам нужно будет написать процедуру синтаксического анализа для извлечения чисел, операторов и всего остального, что вы хотите поддержать в математическом выражении.

итак, нет, нет никакого (простого) способа без eval. Если вы беспокоитесь о безопасности (потому что вход, который вы анализируете, не из источника, который вы контролируете), возможно, вы можете проверить формат ввода (через фильтр регулярных выражений белого списка) перед тем, как передавая его eval?

// вы можете сделать + или - легко:

function addbits(s){
    var total= 0, s= s.match(/[+\-]*(\.\d+|\d+(\.\d+)?)/g) || [];
    while(s.length){
        total+= parseFloat(s.shift());
    }
    return total;
}

var string='1+23+4+5-30';
addbits(string)

более сложная математика делает eval более привлекательным-и, конечно, проще писать.

Я пошел искать библиотеки JavaScript для оценки математических выражений и нашел этих двух перспективных кандидатов:

  • JavaScript Expression Evaluator: меньше и, надеюсь, больше легкий вес. Позволяет алгебраические выражения, замены и a количество функций.

  • mathjs: позволяет комплексные числа, матрицы и блоки, а также. Встроенный для использования как в браузере JavaScript, так и Узел.js.

Я недавно сделал это в C# (нет Eval () для нас...) путем вычисления выражения в обратной польской нотации (это самая легкая). Трудная часть фактически разбирает строку ths и превращает ее в обратную польскую нотацию. Я использовал алгоритм маневрового двора, так как есть отличный пример в Википедии и псевдокоде. Я нашел, что это очень просто реализовать оба, и я бы рекомендовал, если вы еще не нашли решение или ищете альтернативы.

Я создал BigEval С той же целью.
В решении выражений, он выполняет точно так же, как Eval() и поддерживает такие операторы, как%,^,&, * * (мощность) и ! (факторный.) Вы также можете использовать функции и константы (или переменных) внутри выражения. Выражение решается в порядок PEMDAS, который является общим в языках программирования, включая JavaScript.

var Obj = new BigEval();
var result = Obj.exec("5! + 6.6e3 * (PI + E)"); // 38795.17158152233
var result2 = Obj.exec("sin(45 * deg)**2 + cos(pi / 4)**2"); // 1
var result3 = Obj.exec("0 & -7 ^ -7 - 0%1 + 6%2"); //-7

Он также может быть использован для использования этих библиотек большого числа для арифметики в случае, если вы имеете дело с числами с произвольной точностью.

Это небольшая функция, которую я собрал только сейчас, чтобы решить эту проблему - она строит выражение, анализируя строку по одному символу за раз (это на самом деле довольно быстро, хотя). Это займет любое математическое выражение (ограниченное только операторами+, -,*,/) и вернет результат. Он может обрабатывать отрицательные значения и неограниченное количество операций, а также.

единственное, что нужно сделать, это убедиться, что он вычисляет * & / перед + & -. Добавлю эту функциональность позже, но для теперь это то, что мне нужно...

/**
* Evaluate a mathematical expression (as a string) and return the result
* @param {String} expr A mathematical expression
* @returns {Decimal} Result of the mathematical expression
* @example
*    // Returns -81.4600
*    expr("10.04+9.5-1+-100");
*/ 
function expr (expr) {

    var chars = expr.split("");
    var n = [], op = [], index = 0, oplast = true;

    n[index] = "";

    // Parse the expression
    for (var c = 0; c < chars.length; c++) {

        if (isNaN(parseInt(chars[c])) && chars[c] !== "." && !oplast) {
            op[index] = chars[c];
            index++;
            n[index] = "";
            oplast = true;
        } else {
            n[index] += chars[c];
            oplast = false;
        }
    }

    // Calculate the expression
    expr = parseFloat(n[0]);
    for (var o = 0; o < op.length; o++) {
        var num = parseFloat(n[o + 1]);
        switch (op[o]) {
            case "+":
                expr = expr + num;
                break;
            case "-":
                expr = expr - num;
                break;
            case "*":
                expr = expr * num;
                break;
            case "/":
                expr = expr / num;
                break;
        }
    }

    return expr;
}

альтернатива отличному ответу @kennebec, используя более короткое регулярное выражение и позволяя пробелы между операторами

function addbits(s) {
    var total = 0;
    s = s.replace(/\s/g, '').match(/[+\-]?([0-9\.\s]+)/g) || [];
    while(s.length) total += parseFloat(s.shift());
    return total;
}

использовать его как

addbits('5 + 30 - 25.1 + 11');

обновление

вот более оптимизированная версия

function addbits(s) {
    return (s.replace(/\s/g, '').match(/[+\-]?([0-9\.]+)/g) || [])
        .reduce(function(sum, value) {
            return parseFloat(sum) + parseFloat(value);
        });
}

Я в конечном итоге пошел на это решение, которое работает для суммирования положительных и отрицательных целых чисел (и с небольшой модификацией регулярного выражения будет работать и для десятичных чисел):

function sum(string) {
  return (string.match(/^(-?\d+)(\+-?\d+)*$/)) ? string.split('+').stringSum() : NaN;
}   

Array.prototype.stringSum = function() {
    var sum = 0;
    for(var k=0, kl=this.length;k<kl;k++)
    {
        sum += +this[k];
    }
    return sum;
}

Я не уверен, что это быстрее, чем eval (), но поскольку мне приходится выполнять операцию много раз, мне гораздо удобнее запускать этот скрипт, чем создавать множество экземпляров компилятора javascript

попробовать nerdamer

var result = nerdamer('12+2+PI').evaluate();
document.getElementById('text').innerHTML = result.text();
<script src="http://nerdamer.com/js/nerdamer.core.js"></script>
<div id="text"></div>

Попробуйте Автокалькулятор https://github.com/JavscriptLab/autocalculate Вычислите входное значение и выход с помощью выражений селектора

просто добавьте атрибут для вашего выходного входа, например data-ac="(#firstinput+#secondinput)"

нет необходимости в какой-либо инициализации просто добавить атрибут data-ac только. Он автоматически обнаружит динамически добавленные элементы

добавить 'РС' с Выход просто добавить внутрь фигурных скоба data-ac= " {Rs} (#firstinput+#secondinput)"

Я считаю, что parseInt и ES6 может быть полезно в этой ситуации

==> таким образом:

let func = (str) => {
let arr = str.split("");
return `${Number(arr[0]) + parseInt(arr[1] + Number(arr[2]))}`};
console.log(func("1+1"));

главное здесь то, что parseInt анализирует число с помощью оператора. Код может быть изменен в соответствии с соответствующими потребностями.

вот алгоритмическое решение, подобное jMichael, которое циклически проходит через символ выражения за символом и постепенно отслеживает влево/оператор/вправо. Функция накапливает Результат после каждого хода и находит символ оператора. Эта версия поддерживает только операторы ' + 'и' -', но написана для расширения другими операторами. Примечание: мы устанавливаем 'currOp' в '+' перед циклом, потому что мы предполагаем, что выражение начинается с положительного поплавка. На самом деле, в целом я делаю предположение, что ввод аналогичен тому, что будет поступать из калькулятора.

function calculate(exp) {
  const opMap = {
    '+': (a, b) => { return parseFloat(a) + parseFloat(b) },
    '-': (a, b) => { return parseFloat(a) - parseFloat(b) },
  };
  const opList = Object.keys(opMap);

  let acc = 0;
  let next = '';
  let currOp = '+';

  for (let char of exp) {
    if (opList.includes(char)) {
      acc = opMap[currOp](acc, next);
      currOp = char;
      next = '';
    } else {
      next += char;
    } 
  }

  return currOp === '+' ? acc + parseFloat(next) : acc - parseFloat(next);
}
const getAddition = (str) => {
  return str.split('+').reduce((total, num) => (total + num * 1), 0);
};

const addition = getAddition('1+1');

дополнительно 2.

Comments

    Ничего не найдено.