Programing

"3 * (4 + 2)"문자열을 평가하면 int 18 [duplicate]이 생성됩니다.

crosscheck 2020. 8. 26. 07:21
반응형

"3 * (4 + 2)"문자열을 평가하면 int 18 [duplicate]이 생성됩니다.


이 질문에 이미 답변이 있습니다.

문자열에 포함 된 숫자 표현식을 평가하고 결과를 반환 할 수있는 .NET 프레임 워크 함수가 있습니까? Fe :

string mystring = "3*(2+4)";
int result = EvaluateExpression(mystring);
Console.Writeln(result); // Outputs 18

EvaluateExpression방법을 대체 할 수있는 표준 프레임 워크 기능이 있습니까?


예, C # 컴파일러가 런타임에 평가하도록 할 수 있습니다.

참조 : CSharpCorner


문자열 표현식을 평가하려면 아래 코드 스 니펫을 사용하십시오.

using System.Data;

DataTable dt = new DataTable();
var v = dt.Compute("3 * (2+4)","");

컴파일러를 사용하면 생성 된 어셈블리가로드되고 해제되지 않으므로 메모리 누수가 발생합니다. 또한 실제 표현 해석기를 사용하는 것보다 성능이 떨어집니다. 이를 위해 오직이 의도를 가진 오픈 소스 프레임 워크 인 Ncalc사용할 수 있습니다 . 이미 포함 된 변수가 충분하지 않은 경우 고유 한 변수와 사용자 지정 함수를 정의 할 수도 있습니다.

예:

Expression e = new Expression("2 + 3 * 5");
Debug.Assert(17 == e.Evaluate());

이 시도:

static double Evaluate(string expression) {
  var loDataTable = new DataTable();
  var loDataColumn = new DataColumn("Eval", typeof (double), expression);
  loDataTable.Columns.Add(loDataColumn);
  loDataTable.Rows.Add(0);
  return (double) (loDataTable.Rows[0]["Eval"]);
}

"XpathNavigator.Evaluate"를 볼 수 있습니다. 저는 이것을 사용하여 GridView의 수학적 표현을 처리했으며 저에게 잘 작동합니다.

내 프로그램에 사용한 코드는 다음과 같습니다.

public static double Evaluate(string expression)
{
    return (double)new System.Xml.XPath.XPathDocument
    (new StringReader("<r/>")).CreateNavigator().Evaluate
    (string.Format("number({0})", new
    System.Text.RegularExpressions.Regex(@"([\+\-\*])")
    .Replace(expression, " ${1} ")
    .Replace("/", " div ")
    .Replace("%", " mod ")));
}

static double Evaluate(string expression) { 
  var loDataTable = new DataTable(); 
  var loDataColumn = new DataColumn("Eval", typeof (double), expression); 
  loDataTable.Columns.Add(loDataColumn); 
  loDataTable.Rows.Add(0); 
  return (double) (loDataTable.Rows[0]["Eval"]); 
} 

작동 원리에 대한 설명 :

먼저 var loDataTable = new DataTable();데이터베이스 엔진 (예 : MS SQL)에서와 같이 part에 테이블을 만듭니다 .

그런 다음 특정 매개 변수 ( var loDataColumn = new DataColumn("Eval", typeof (double), expression);)가 있는 열 입니다.

"Eval"파라미터는 열 (의 ColumnName 속성)의 이름이다.

typeof (double)열에 저장할 데이터 유형으로 System.Type.GetType("System.Double");대신 put과 같습니다 .

expressionEvaluate메소드가 수신 Expression하고 열의 속성 저장 되는 문자열입니다 . 이 속성은 열에있는 모든 행이 "표현식"으로 채워지고 SQL 쿼리에 넣을 수있는 거의 모든 행을 수용한다는 것입니다. Expression 속성에 넣을 수있는 내용과 평가 방법을 알아 보려면 http://msdn.microsoft.com/en-us/library/system.data.datacolumn.expression(v=vs.100).aspx참조하십시오 .

그런 다음 테이블에 loDataTable.Columns.Add(loDataColumn);loDataColumn추가 loDataTable합니다.

그런 다음을 통해 수행되는 Expression 속성이있는 개인화 된 열이있는 테이블에 행이 추가됩니다 loDataTable.Rows.Add(0);. 이 행을 추가하면 테이블의 "Eval"열의 셀이 loDataTable"Expression"속성으로 자동으로 채워지고 연산자 및 SQL 쿼리 등이있는 경우 평가 된 다음 셀에 저장되므로 여기서 "마법"이 발생하고 연산자가있는 문자열이 평가되고 셀에 저장됩니다.

마지막으로 행 0의 "Eval"열의 셀에 저장된 값을 반환하고 (색인이며 0부터 계산 시작)를 사용하여 double로 변환합니다 return (double) (loDataTable.Rows[0]["Eval"]);.

그리고 그게 전부입니다.

그리고 여기에 이해하기 쉬운 코드가 있습니다. 이것은 똑같은 일을합니다. 그것은 메소드 내부에 있지 않고, 또한 설명되어 있습니다.

DataTable MyTable = new DataTable();
DataColumn MyColumn = new DataColumn();
MyColumn.ColumnName = "MyColumn";
MyColumn.Expression = "5+5/5"
MyColumn.DataType = typeof(double);
MyTable.Columns.Add(MyColumn);
DataRow MyRow = MyTable.NewRow();
MyTable.Rows.Add(MyRow);
return (double)(MyTable.Rows[0]["MyColumn"]);

먼저 다음을 사용하여 테이블을 만듭니다. DataTable MyTable = new DataTable();

그런 다음 DataColumn MyColumn = new DataColumn();

다음으로 열에 이름을 입력합니다. 이렇게하면 테이블에 저장 될 때 내용을 검색 할 수 있습니다. 다음을 통해 완료MyColumn.ColumnName = "MyColumn";

그런 다음 표현식에 문자열 유형의 변수를 넣을 수 있습니다.이 경우 사전 정의 된 문자열 "5 + 5 / 5"가 있으며 결과는 6입니다.

열에 저장할 데이터 유형 MyColumn.DataType = typeof(double);

테이블에 열 추가 ... MyTable.Columns.Add(MyColumn);

테이블 구조를 복사하는 테이블에 삽입 할 행을 만듭니다. DataRow MyRow = MyTable.NewRow();

다음을 사용하여 테이블에 행 추가 MyTable.Rows.Add(MyRow);

그리고 열의 행 0에서 셀의 값을 반환하는 MyColumn테이블 MyTablereturn (double)(MyTable.Rows[0]["MyColumn"]);

레슨 완료 !!!


이것은 스택을 사용하는 간단한 표현식 평가 기입니다.

public class MathEvaluator
{
    public static void Run()
    {
        Eval("(1+2)");
        Eval("5*4/2");
        Eval("((3+5)-6)");
    }

    public static void Eval(string input)
    {
        var ans = Evaluate(input);
        Console.WriteLine(input + " = " + ans);
    }

    public static double Evaluate(String input)
    {
        String expr = "(" + input + ")";
        Stack<String> ops = new Stack<String>();
        Stack<Double> vals = new Stack<Double>();

        for (int i = 0; i < expr.Length; i++)
        {
            String s = expr.Substring(i, 1);
            if (s.Equals("(")){}
            else if (s.Equals("+")) ops.Push(s);
            else if (s.Equals("-")) ops.Push(s);
            else if (s.Equals("*")) ops.Push(s);
            else if (s.Equals("/")) ops.Push(s);
            else if (s.Equals("sqrt")) ops.Push(s);
            else if (s.Equals(")"))
            {
                int count = ops.Count;
                while (count > 0)
                {
                    String op = ops.Pop();
                    double v = vals.Pop();
                    if (op.Equals("+")) v = vals.Pop() + v;
                    else if (op.Equals("-")) v = vals.Pop() - v;
                    else if (op.Equals("*")) v = vals.Pop()*v;
                    else if (op.Equals("/")) v = vals.Pop()/v;
                    else if (op.Equals("sqrt")) v = Math.Sqrt(v);
                    vals.Push(v);

                    count--;
                }
            }
            else vals.Push(Double.Parse(s));
        }
        return vals.Pop();
    }
}

이것은 오른쪽에서 왼쪽으로 실행되므로 표현을 실행하려면 적절한 패러 테 시스를 사용해야합니다.

    // 2+(100/5)+10 = 32
    //((2.5+10)/5)+2.5 = 5
    // (2.5+10)/5+2.5 = 1.6666
    public static double Evaluate(String expr)
    {

        Stack<String> stack = new Stack<String>();

        string value = "";
        for (int i = 0; i < expr.Length; i++)
        {
            String s = expr.Substring(i, 1);
            char chr = s.ToCharArray()[0];

            if (!char.IsDigit(chr) && chr != '.' && value != "")
            {
                stack.Push(value);
                value = "";
            }

            if (s.Equals("(")) {

                string innerExp = "";
                i++; //Fetch Next Character
                int bracketCount=0;
                for (; i < expr.Length; i++)
                {
                    s = expr.Substring(i, 1);

                    if (s.Equals("("))
                        bracketCount++;

                    if (s.Equals(")"))
                        if (bracketCount == 0)
                            break;
                        else
                            bracketCount--;


                    innerExp += s;
                }

                stack.Push(Evaluate(innerExp).ToString());

            }
            else if (s.Equals("+")) stack.Push(s);
            else if (s.Equals("-")) stack.Push(s);
            else if (s.Equals("*")) stack.Push(s);
            else if (s.Equals("/")) stack.Push(s);
            else if (s.Equals("sqrt")) stack.Push(s);
            else if (s.Equals(")"))
            {
            }
            else if (char.IsDigit(chr) || chr == '.')
            {
                value += s;

                if (value.Split('.').Length > 2)
                    throw new Exception("Invalid decimal.");

                if (i == (expr.Length - 1))
                    stack.Push(value);

            }
            else
                throw new Exception("Invalid character.");

        }


        double result = 0;
        while (stack.Count >= 3)
        {

            double right = Convert.ToDouble(stack.Pop());
            string op = stack.Pop();
            double left = Convert.ToDouble(stack.Pop());

            if (op == "+") result = left + right;
            else if (op == "+") result = left + right;
            else if (op == "-") result = left - right;
            else if (op == "*") result = left * right;
            else if (op == "/") result = left / right;

            stack.Push(result.ToString());
        }


        return Convert.ToDouble(stack.Pop());
    }

적절한 fluff 래핑 (기본적으로 유형 및 메서드)을 사용하여 CSharpCodeProvider를 통해이 작업을 상당히 쉽게 실행할 수 있습니다. 마찬가지로 다른 답변이 제안했듯이 VB 등 또는 JavaScript를 사용할 수 있습니다. 이 시점에서 프레임 워크에 내장 된 다른 어떤 것도 알지 못합니다.

나는 동적 언어를 지원하는 .NET 4.0이이면에서 더 나은 기능을 가질 것이라고 기대합니다.


최근에 프로젝트를 위해이 작업을 수행해야했고 결국 IronPython사용 하여 작업을 수행했습니다. 엔진의 인스턴스를 선언 한 다음 유효한 파이썬 표현식을 전달하고 결과를 얻을 수 있습니다. 간단한 수학 표현 만하면 충분합니다. 내 코드는 다음과 비슷하게 보입니다.

IronPython.Hosting.PythonEngine pythonEngine = new IronPython.Hosting.PythonEngine();
string expression = "3*(2+4)";
double result = pythonEngine.EvaluateAs<double>(expression);

각 표현식에 대한 엔진을 만들고 싶지 않을 것입니다. IronPython.dll에 대한 참조도 필요합니다.


편집 : 나는 그것을 조금 더 BODMAS를 준수하기 위해 별도로 더하기와 빼기를 별도로 가져와야한다는 것을 깨달았습니다.

스택 기반 접근 방식에 대해 Rajesh Jinaga에게 큰 감사를 표합니다. 내 필요에 정말 유용하다는 것을 알았습니다. 다음 코드는 먼저 나눗셈을 처리 한 다음 곱셈을 처리 한 다음 더하기와 빼기로 마무리하는 Rajesh의 방법을 약간 수정 한 것입니다. 또한 표현식에서 부울을 사용할 수 있습니다. 여기서 true는 1과 false 0으로 처리됩니다. 표현식에서 부울 논리를 사용할 수 있습니다.

public static double Evaluate(string expr)
    {
        expr = expr.ToLower();
        expr = expr.Replace(" ", "");
        expr = expr.Replace("true", "1");
        expr = expr.Replace("false", "0");

        Stack<String> stack = new Stack<String>();

        string value = "";
        for (int i = 0; i < expr.Length; i++)
        {
            String s = expr.Substring(i, 1);
            // pick up any doublelogical operators first.
            if (i < expr.Length - 1)
            {
                String op = expr.Substring(i, 2);
                if (op == "<=" || op == ">=" || op == "==")
                {
                    stack.Push(value);
                    value = "";
                    stack.Push(op);
                    i++;
                    continue;
                }
            }

            char chr = s.ToCharArray()[0];

            if (!char.IsDigit(chr) && chr != '.' && value != "")
            {
                stack.Push(value);
                value = "";
            }
            if (s.Equals("("))
            {
                string innerExp = "";
                i++; //Fetch Next Character
                int bracketCount = 0;
                for (; i < expr.Length; i++)
                {
                    s = expr.Substring(i, 1);

                    if (s.Equals("(")) bracketCount++;

                    if (s.Equals(")"))
                    {
                        if (bracketCount == 0) break;
                        bracketCount--;
                    }
                    innerExp += s;
                }
                stack.Push(Evaluate(innerExp).ToString());
            }
            else if (s.Equals("+") ||
                     s.Equals("-") ||
                     s.Equals("*") ||
                     s.Equals("/") ||
                     s.Equals("<") ||
                     s.Equals(">"))
            {
                stack.Push(s);
            }
            else if (char.IsDigit(chr) || chr == '.')
            {
                value += s;

                if (value.Split('.').Length > 2)
                    throw new Exception("Invalid decimal.");

                if (i == (expr.Length - 1))
                    stack.Push(value);

            }
            else
            {
                throw new Exception("Invalid character.");
            }

        }
        double result = 0;
        List<String> list = stack.ToList<String>();
        for (int i = list.Count - 2; i >= 0; i--)
        {
            if (list[i] == "/")
            {
                list[i] = (Convert.ToDouble(list[i - 1]) / Convert.ToDouble(list[i + 1])).ToString();
                list.RemoveAt(i + 1);
                list.RemoveAt(i - 1);
                i -= 2;
            }
        }

        for (int i = list.Count - 2; i >= 0; i--)
        {
            if (list[i] == "*")
            {
                list[i] = (Convert.ToDouble(list[i - 1]) * Convert.ToDouble(list[i + 1])).ToString();
                list.RemoveAt(i + 1);
                list.RemoveAt(i - 1);
                i -= 2;
            }
        }
        for (int i = list.Count - 2; i >= 0; i--)
        {
            if (list[i] == "+")
            {
                list[i] = (Convert.ToDouble(list[i - 1]) + Convert.ToDouble(list[i + 1])).ToString();
                list.RemoveAt(i + 1);
                list.RemoveAt(i - 1);
                i -= 2;
            }
        }
        for (int i = list.Count - 2; i >= 0; i--)
        {
            if (list[i] == "-")
            {
                list[i] = (Convert.ToDouble(list[i - 1]) - Convert.ToDouble(list[i + 1])).ToString();
                list.RemoveAt(i + 1);
                list.RemoveAt(i - 1);
                i -= 2;
            }
        }
        stack.Clear();
        for (int i = 0; i < list.Count; i++)
        {
            stack.Push(list[i]);
        }
        while (stack.Count >= 3)
        {
            double right = Convert.ToDouble(stack.Pop());
            string op = stack.Pop();
            double left = Convert.ToDouble(stack.Pop());

            if (op == "<") result = (left < right) ? 1 : 0;
            else if (op == ">") result = (left > right) ? 1 : 0;
            else if (op == "<=") result = (left <= right) ? 1 : 0;
            else if (op == ">=") result = (left >= right) ? 1 : 0;
            else if (op == "==") result = (left == right) ? 1 : 0;

            stack.Push(result.ToString());
        }
        return Convert.ToDouble(stack.Pop());
    }

나는 그것을하는 더 깨끗한 방법이있을 가능성이 있음을 알고 있으며, 누군가가 유용하다고 생각할 경우를 대비하여 이드가 첫 번째 모습을 공유한다고 생각했습니다.


Ramesh에게 감사드립니다. 저는 그의 간단한 코드 버전을 사용하여 데이터베이스에서 문자열을 꺼내 내 코드에서 부울 연산을 수행하는 데 사용했습니다.

x는 1500 또는 2100과 같은 숫자입니다.

함수는 x> 1400 및 x <1600과 같은 저장된 평가입니다.

function = relation[0].Replace("and","&&").Replace("x",x);

DataTable f_dt = new DataTable();
var f_var = f_dt.Compute(function,"");

if (bool.Parse(f_var.ToString()) { do stuff  }

없기. 일부 외부 라이브러리를 사용하거나 자체 파서를 작성해야합니다. 그렇게 할 시간이 있다면 꽤 흥미로운 프로젝트이므로 직접 파서를 작성하는 것이 좋습니다. 그렇지 않으면 bcParser 와 같은 것을 사용해야합니다 .


짧은 대답 : 그렇게 생각하지 않습니다. C # .Net은 컴파일되어 (바이트 코드로) 내가 아는 한 런타임에 문자열을 평가할 수 없습니다. 그러나 JScript .Net은 가능합니다. 그러나 여전히 파서 및 스택 기반 평가자를 직접 코딩하는 것이 좋습니다.

참고 URL : https://stackoverflow.com/questions/333737/evaluating-string-342-yield-int-18

반응형