JavaScript에서 문자열을 수학 표현식으로 평가


82

숫자 값을 생성하기 위해 '1+1'호출하지 않고 문자열 (예 :)에서 수학 표현식을 구문 분석하고 평가하는 방법은 eval(string)무엇입니까?

이 예제에서는 함수가을 받아들이고 '1+1'반환 하기를 원합니다 2.


5
매우 유사하지만 그것은 당신이 요구하는지 아마 다음과 같습니다 (Function("return 1+1;"))().
Gumbo

답변:



22

+ 또는-를 쉽게 수행 할 수 있습니다.

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';
console.log(
  addbits(string)
)

더 복잡한 수학은 eval을 더 매력적으로 만들고 확실히 쓰기를 더 간단하게 만듭니다.


2
+1-아마도 내가 함께했던 것보다 조금 더 일반적이지만 1 + -2와 같은 것을 가질 수 있기 때문에 내 상황에서는 작동하지 않을 것이며 정규식이 잘못된 문도 제외하기를 원합니다 (나는 당신의 뭔가 "+ 3 + 4 +")
wheresrhys

더 짧은 정규식과 연산자 사이에 공백을 허용하는 업데이트 된 답변 아래에 게시했습니다
Stefan Gabos

17

누군가 그 문자열을 파싱해야합니다. 인터프리터가 아니라면 (을 통해 eval) 숫자, 연산자 및 수학 표현식에서 지원하려는 다른 모든 것을 추출하는 구문 분석 루틴을 작성하는 것이 당신이어야합니다.

따라서 .NET 없이는 (간단한) 방법이 없습니다 eval. 보안에 대해 우려하는 경우 (파싱하는 입력이 제어하는 ​​소스가 아니기 때문에) eval?에 전달하기 전에 입력의 형식 (화이트리스트 정규식 필터를 통해)을 확인할 수 있습니다 .


1
나를 괴롭히는 것은 보안이 아닙니다 (이미 작업에 대한 정규 표현식이 있습니다). 이와 같은 많은 문자열을 처리해야하기 때문에 브라우저에 더 많은 부하가 걸립니다. 사용자 정의 파서가 eval ()보다 빠를 수 있습니까?
wheresrhys 2010

11
@wheresrhys : JS로 작성된 파서가 시스템에서 제공하는 것보다 빠를 것이라고 생각하는 이유는 무엇입니까 (최적화, 아마도 C 또는 C ++로 작성)?
Mehrdad Afshari

4
eval은이를 수행하는 가장 빠른 방법입니다. 그러나 정규 표현식은 일반적으로 보안을 보장하기에 충분하지 않습니다.
levik 2010

1
@wheresrhys : 왜 이런 문자열이 많은가요? 프로그램에 의해 생성되고 있습니까? 그렇다면 가장 간단한 방법은 결과를 문자열로 변환하기 전에 계산하는 것입니다. 그렇지 않으면 파서 작성 시간입니다.
Phil H

12

더 짧은 정규 표현식을 사용하고 연산자 사이에 공백을 허용하는 @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);
        });
}

1
덧셈과 뺄셈 만 필요하면 완벽합니다. 코드가 적고 제품이 너무 많습니다! 안심하십시오, 그것은 선을 위해 사용되고 있습니다 :)
Ultroman the Tacoman

10

같은 목적으로 BigEval 을 만들었습니다 .
식을 풀 때 Eval()%, ^, &, ** (승수) 및! (계승). 표현식 내에서 함수와 상수 (또는 변수)를 사용할 수도 있습니다. 표현식은 JavaScript를 포함한 프로그래밍 언어에서 일반적으로 사용되는 PEMDAS 순서 로 해결됩니다 .

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

임의의 정밀도로 숫자를 처리하는 경우 산술에 해당 Big Number 라이브러리를 사용하도록 만들 수도 있습니다.


8

수학적 표현을 평가하기 위해 JavaScript 라이브러리를 찾고 있었고 다음 두 가지 유망한 후보를 찾았습니다.

  • JavaScript Expression Evaluator : 더 작고 가볍습니다. 대수식, 대체 및 여러 함수를 허용합니다.

  • mathjs : 복소수, 행렬 및 단위도 허용합니다. 브라우저 내 JavaScript 및 Node.js 모두에서 사용하도록 제작되었습니다.


이제 JavaScript Expression Evaluator를 테스트했는데 흔들리는 것 같습니다. (mathjs 아마 너무 바위,하지만 내 목적을 위해 조금 너무 크고 나는 또한 JSEE의 대체 기능처럼 보인다.)
Itangalo

7

저는 최근 Eval()Reverse Polish Notation 의 표현을 평가하여 C #에서이 작업을 수행했습니다 ( 우리에게는 아닙니다 ... ). 어려운 부분은 실제로 문자열을 구문 분석하여 Reverse Polish Notation으로 바꾸는 것입니다. Wikipedia 및 의사 코드에 대한 훌륭한 예가 있으므로 Shunting Yard 알고리즘을 사용했습니다 . 둘 다 구현하는 것이 정말 간단하다는 것을 알았고 아직 해결책을 찾지 못했거나 대안을 찾고 있다면 이것을 권장합니다.


Wikipedia에 대한 몇 가지 예 또는 링크를 제공 할 수 있습니까?
LetynSOFT

의사 코드 @LetynSOFT 찾을 수 있습니다 여기에
Mayonnaise2124

6

이것은이 문제를 해결하기 위해 방금 모아 놓은 작은 함수입니다. 한 번에 한 문자 씩 문자열을 분석하여 표현식을 작성합니다 (실제로는 꽤 빠릅니다). 이것은 모든 수학 식 (+,-, *, / 연산자로만 제한됨)을 취하고 결과를 반환합니다. 음수 값과 무제한 숫자 연산도 처리 할 수 ​​있습니다.

남은 유일한 "해야 할 일"은 + &-전에 * & /를 계산하는 것입니다. 나중에 해당 기능을 추가 할 예정이지만 지금은 필요한 작업을 수행합니다.

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

3

간단하고 우아한 Function()

function parse(str) {
  return Function(`'use strict'; return (${str})`)()
}

parse("1+2+3"); 


어떻게 작동하는지 설명해 주시겠습니까? 저는이 구문을
처음 접했습니다

Function ( "return (1 + 2 + 3)") (); -익명의 기능입니다. 우리는 단지 인자 (함수 본문)를 실행하고 있습니다. Function ( "{return (1 + 2 + 3)}") ();
Aniket Kudale

좋아 어떻게 문자열이 구문 분석됩니까? & 그게 뭐야 ($ {str}) ) -----()`마지막으로이 대괄호?
pageNotfoUnd

이것이 eval보다 얼마나 나은지 모르겠습니다. 이 서버 측을 실행하기 전에 parse('process.exit()').
바스 티

3

for 루프를 사용하여 문자열에 잘못된 문자가 포함되어 있는지 확인한 다음 eval과 함께 try ... catch를 사용하여 계산에서 원하는 오류가 발생하는지 확인할 eval("2++")수 있습니다.

function evaluateMath(str) {
  for (var i = 0; i < str.length; i++) {
    if (isNaN(str[i]) && !['+', '-', '/', '*', '%', '**'].includes(str[i])) {
      return NaN;
    }
  }
  
  
  try {
    return eval(str)
  } catch (e) {
    if (e.name !== 'SyntaxError') throw e
    return NaN;
  }
}

console.log(evaluateMath('2 + 6'))

또는 함수 대신 설정할 수 있습니다. Math.eval

Math.eval = function(str) {
  for (var i = 0; i < str.length; i++) {
    if (isNaN(str[i]) && !['+', '-', '/', '*', '%', '**'].includes(str[i])) {
      return NaN;
    }
  }
  
  
  try {
    return eval(str)
  } catch (e) {
    if (e.name !== 'SyntaxError') throw e
    return NaN;
  }
}

console.log(Math.eval('2 + 6'))


2

결국 양수와 음수를 합산하는이 솔루션을 사용했습니다 (정규식을 약간 수정하면 십진수에서도 작동합니다).

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 ()보다 빠른지 확실하지 않지만 작업을 여러 번 수행해야하므로 자바 스크립트 컴파일러의 인스턴스를 생성하는 것보다이 스크립트를 실행하는 것이 훨씬 더 편합니다.


1
하지만 return표현의 내부에 사용할 수 없습니다, sum("+1")반환 NaN이를 .
Gumbo

return이 삼항 표현식 안에 들어가야할지 안 될지 항상 예고합니다. "+1"은 숫자로 평가해야하지만 실제로는 일상적인 의미에서 수학적 합계의 예가 아니기 때문에 제외하고 싶습니다. 내 코드는 허용되는 문자열을 평가하고 필터링하도록 설계되었습니다.
wheresrhys 2010 년

2

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

나는 그렇게 생각 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

AutoCalculator https://github.com/JavscriptLab/autocalculate Calculate Inputs value and Output By using selector expressions

data-ac = "(# firstinput + # secondinput)"과 같은 출력 입력에 속성을 추가하기 만하면됩니다.

초기화 할 필요가 없습니다. data-ac 속성 만 추가하면됩니다. 동적으로 추가 된 요소를 자동으로 찾습니다.

또는 출력에 'Rs'를 추가하려면 중괄호 안에 추가하십시오. data-ac = "{Rs} (# firstinput + # secondinput)"


1
const operatorToFunction = {
    "+": (num1, num2) => +num1 + +num2,
    "-": (num1, num2) => +num1 - +num2,
    "*": (num1, num2) => +num1 * +num2,
    "/": (num1, num2) => +num1 / +num2
}

const findOperator = (str) => {
    const [operator] = str.split("").filter((ch) => ["+", "-", "*", "/"].includes(ch))
    return operator;
}

const executeOperation = (str) => {
    const operationStr = str.replace(/[ ]/g, "");
    const operator = findOperator(operationStr);
    const [num1, num2] = operationStr.split(operator)
    return operatorToFunction[operator](num1, num2);
};

const addition = executeOperation('1 + 1'); // ans is 2
const subtraction = executeOperation('4 - 1'); // ans is 3
const multiplication = executeOperation('2 * 5'); // ans is 10
const division = executeOperation('16 / 4'); // ans is 4

1
빼기, 곱하기, 나누기는 어떻습니까? 왜 num1을 곱 해야합니까?
nathanfranke

@nathanfranke를 지적 해 주셔서 감사합니다. 좀 더 일반적으로 만들기 위해 답변을 업데이트했습니다. 이제 4 가지 작업을 모두 지원합니다. 그리고 1의 배수는 그것을 문자열에서 숫자로 변환하는 것입니다. + num을 수행하여 얻을 수 있습니다.
Rushikesh Bharad

0

다음은 문자별로 표현 문자를 반복하고 왼쪽 / 연산자 / 오른쪽을 점진적으로 추적하는 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);
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.