Подтвердить что ты не робот

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

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

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

4b9b3361

Ответ 1

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

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(), но поскольку я должен выполнять операцию много раз, мне гораздо удобнее запускать этот script, чем создавать множество экземпляров компилятора javascript

Ответ 2

Вы можете использовать библиотеку JavaScript Expression Evaluator, которая позволяет вам делать такие вещи, как:

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

Или mathjs, что позволяет использовать такие вещи, как:

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

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

Ответ 3

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

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 более привлекательной и, безусловно, проще писать.

Ответ 4

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

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

Ответ 5

Я создал BigEval для той же цели.
При решении выражений он работает точно так же, как Eval() и поддерживает такие операторы, как%, ^, &, ** (power) и! (факториал). Вы также можете использовать функции и константы (или, скажем, переменные) внутри выражения. Выражение решается в порядке 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

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

Ответ 6

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

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

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

Ответ 7

Недавно я сделал это в С# (без Eval() для нас...), оценив выражение в Reverse Polish Notation (это легкий бит). Трудная часть на самом деле разбирает строку и превращает ее в обратную польскую нотацию. Я использовал алгоритм Shunting Yard как отличный пример в Википедии и псевдокоде. Мне было очень просто реализовать оба варианта, и я бы рекомендовал это, если вы еще не нашли решение или не смотрите на альтернативы.

Ответ 8

Альтернатива отличному ответу @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);
        });
}

Ответ 9

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

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

/**
* 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;
}

Ответ 10

Попробуйте 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>

Ответ 11

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

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

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

FOr add 'Rs' с выходом просто добавьте внутреннюю фигурную скобку Данные-ас = "{} рупий (# firstinput + # secondinput)"

Ответ 12

Я считаю, что 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 анализирует число с помощью оператора. Код может быть изменен с учетом соответствующих потребностей.

Ответ 13

Вот алгоритмическое решение, подобное jMichael, которое перебирает выражение символ за символом и постепенно отслеживает left/operator/right. Функция накапливает результат после каждого хода, она находит символ оператора. Эта версия поддерживает только операторы "+" и "-", но написана для расширения с другими операторами. Примечание: мы устанавливаем '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);
}

Ответ 14

const getAddition = (str) => {
  return str.split('+').reduce((total, num) => (total + num * 1), 0);
};

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

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