web-dev-qa-db-ja.com

JavaScriptで文字列を数式として評価する

eval(string)を呼び出して数値を生成せずに、文字列内の数式(たとえば'1+1')を解析および評価するにはどうすればよいですか?

その例では、'1+1'を受け入れて2を返す関数が必要です。

60
wheresrhys

私は最終的にこのソリューションに行きましたが、これは正と負の整数を加算するために機能します(そして正規表現に少し修正を加えると小数でも動作します):

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コンパイラのインスタンスのロードを作成するよりもこのスクリプトを実行する方がはるかに快適です

2
wheresrhys

JavaScript Expression Evaluator library を使用すると、次のようなことができます。

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

または mathjs 、次のようなものを許可します:

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

私はプロジェクトの1つにmathjsを選択することになりました。

56
Rafael Vega

//簡単に+または-を実行できます:

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をより魅力的にし、間違いなく簡単に記述できます。

21
kennebec

誰かがその文字列を解析する必要があります。インタープリターではない場合(evalを使用)、数値、演算子、および数式でサポートする他のものを抽出する解析ルーチンを作成する必要があります。

ですから、いいえ、evalなしの(単純な)方法はありません。セキュリティを心配している場合(解析している入力は管理しているソースからのものではないため)、eval

17
bdukes

同じ目的で 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

また、任意の精度で数値を処理する場合に、算術にこれらのビッグナンバーライブラリを使用することもできます。

9
Avi

@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);
        });
}
8
Stefan Gabos

数式を評価するためのJavaScriptライブラリを探して、次の2つの有望な候補を見つけました。

  • JavaScript Expression Evaluator :より小さく、できればより軽量です。代数式、置換、および多数の関数を許可します。

  • mathjs :複素数、行列、単位も使用できます。ブラウザ内のJavaScriptとNode.jsの両方で使用されるように構築されています。

7
Itangalo

私は最近、C#でこれを行いました(Eval()はありません...)、逆ポーランド記法で表現を評価しました(簡単です)。難しい部分は、実際に文字列を解析し、逆ポーランド記法に変換することです。ウィキペディアと擬似コードに素晴らしい例があるので、私はShunting Yardアルゴリズムを使用しました。両方を実装するのは非常に簡単であることがわかったので、まだ解決策を見つけていない場合や代替案を検討している場合はお勧めします。

6
RichK

これは、この問題を解決するために今まとめた小さな関数です-文字列を一度に1文字ずつ分析することで式を作成します(実際にはかなり速いです)。これは、任意の数式(+、-、*、/演算子のみに限定)を取り、結果を返します。負の値と無制限の数の操作も処理できます。

残っている唯一の「やるべきこと」は、+&-の前に*&/を計算することです。後でその機能を追加しますが、今のところこれは私が必要なことを行います...

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

試す 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>
2
ArchHaskeller

AutoCalculatorを試す https://github.com/JavscriptLab/autocalculate セレクター式を使用して入力値と出力を計算する

Data-ac = "(#firstinput +#secondinput)"のような出力入力の属性を追加するだけです

初期化の必要はありません。data-ac属性のみを追加するだけです。動的に追加された要素を自動的に検出します

または、出力付きの「R」を追加するだけで、中括弧内に追加しますdata-ac = "{Rs}(#firstinput +#secondinput)"

1
Justin Jose

この状況では、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が演算子で数値を解析することです。コードは、対応するニーズに合わせて変更できます。

1
fuser

以下は、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);
}
0
internetross
const getAddition = (str) => {
  return str.split('+').reduce((total, num) => (total + num * 1), 0);
};

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

加算は2です。

0