Evaluating string "3*(4+2)" yield int 18 [duplicate]
Asked Answered
V

13

123

Is there a function the .NET framework that can evaluate a numeric expression contained in a string and return the result? F.e.:

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

Is there a standard framework function that you can replace my EvaluateExpression method with?

Viridissa answered 2/12, 2008 at 11:57 Comment(0)
P
199

If you want to evaluate a string expression use the below code snippet.

using System.Data;

DataTable dt = new DataTable();
var v = dt.Compute("3 * (2+4)","");
Phloem answered 14/6, 2012 at 9:3 Comment(7)
I have gotten a lot of use out of this answer when applied to old school .Net, but the new Core framework does not have DataTables. Is there anything that can do this in .Net Core?Smoothie
.NET Core now supports DataTable: blogs.msdn.microsoft.com/devfish/2017/05/15/…Boogeyman
Feels dirty to use a data table to do a simple math calculation. NCalc or some other math-specific library is going to be a better idea iMO.Bisson
interestingly, the current framwork implementation does not rely on existing components either. The System.Data namespace has its own (internal) implementation, a fully-fledged lexer/parser with relational data-oriented capabilitiesObscure
this is by far the best, easiest, and fastest solution with minimal overhead. I would +2 if I couldIdyllic
Well! If you are giving interview and they ask you to write Algorithm for this, then surely using this approach will mark your rejection :) Was expecting some algorithmSanbo
Seems like this doesn't work with modulo operator and floating point values: "3.1415 % 1.0" throws "System.Data.EvaluateException: Cannot perform 'Mod' operation on System.Decimal and System.Decimal.". Unless there is some way to force the DataTable to interpret the floating point values as doubles?Ares
L
85

Using the compiler to do implies memory leaks as the generated assemblies are loaded and never released. It's also less performant than using a real expression interpreter. For this purpose you can use Ncalc which is an open-source framework with this solely intent. You can also define your own variables and custom functions if the ones already included aren't enough.

Example:

Expression e = new Expression("2 + 3 * 5");
Debug.Assert(17 == e.Evaluate());
Lodi answered 14/10, 2009 at 7:47 Comment(2)
It's also a huge security risk to do use the compiler. You intend to allow a numeric expression, but suddenly you've exposed something more like PowerShellInference
Does this solution also help for operators like Min, Max, Avg?Nona
S
52

Try this:

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"]);
}
Spruik answered 13/9, 2009 at 11:13 Comment(2)
Can someone explain why this works?Cardew
@ChrisTrombley: DataColumn.Expression property support a small language that contains basic arithmetic operators and a few useful functions. What @Petar is doing is to create a new column with its Expression property set to the specified expression. Afterwards, when he accesses that column's value, DataTable evaluates the expression and computes the value, which is then returned to the caller. For details of the operators and functions that are supported, see msdn.microsoft.com/en-us/library/….Tonguelash
R
16

You could look at "XpathNavigator.Evaluate" I have used this to process mathematical expressions for my GridView and it works fine for me.

Here is the code I used for my program:

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 ")));
}
Roadstead answered 13/9, 2009 at 10:47 Comment(0)
V
16

This is a simple Expression Evaluator using Stacks

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();
    }
}
Vaso answered 16/11, 2010 at 14:54 Comment(9)
Well its good. But it doesn't follow the arithmatic priority like within bracks it should first calculate division then multiplication then addition and so on.Sulphanilamide
Noticed it only work with one digit numbers.Alexalexa
(4+3)/2 fails (answer comes out as 2).Numb
@DanW try: "((4+3)/2)"Vaso
sqrt will never be evaluated since the op is limited to a single charSubinfeudate
Also this cannot handle 2-digit numbersApocrypha
How can I convert this method - double Evaluate(String input) into an asynchronous method?Munguia
@VSS You put the async keyword in front of it...Redhanded
I have found incorrect results when multiply is used. Precedence is ignored 2*3+2 10 //incorrect 2*3-2 2 //iincorrectGourmand
I
15
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"]); 
} 

Explanation of how it works:

First, we make a table in the part var loDataTable = new DataTable();, just like in a Data Base Engine (MS SQL for example).

Then, a column, with some specific parameters (var loDataColumn = new DataColumn("Eval", typeof (double), expression);).

The "Eval" parameter is the name of the column (ColumnName attribute).

typeof (double) is the type of data to be stored in the column, which is equal to put System.Type.GetType("System.Double"); instead.

expression is the string that the Evaluate method receives, and is stored in the attribute Expression of the column. This attribute is for a really specific purpose (obvious), which is that every row that's put on the column will be fullfilled with the "Expression", and it accepts practically wathever can be put in a SQL Query. Refer to http://msdn.microsoft.com/en-us/library/system.data.datacolumn.expression(v=vs.100).aspx to know what can be put in the Expression attribute, and how it's evaluated.

Then, loDataTable.Columns.Add(loDataColumn); adds the column loDataColumn to the loDataTable table.

Then, a row is added to the table with a personalized column with a Expression attribute, done via loDataTable.Rows.Add(0);. When we add this row, the cell of the column "Eval" of the table loDataTable is fullfilled automatically with its "Expression" attribute, and, if it has operators and SQL Queries, etc, it's evaluated and then stored to the cell, so, here happens the "magic", the string with operators is evaluated and stored to a cell...

Finally, just return the value stored to the cell of the column "Eval" in row 0 (it's an index, starts counting from zero), and making a conversion to a double with return (double) (loDataTable.Rows[0]["Eval"]);.

And that's all... job done!

And here a code eaiser to understand, which does the same... It's not inside a method, and it's explained too.

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"]);

First, create the table with DataTable MyTable = new DataTable();

Then, a column with DataColumn MyColumn = new DataColumn();

Next, we put a name to the column. This so we can search into it's contents when it's stored to the table. Done via MyColumn.ColumnName = "MyColumn";

Then, the Expression, here we can put a variable of type string, in this case there's a predefined string "5+5/5", which result is 6.

The type of data to be stored to the column MyColumn.DataType = typeof(double);

Add the column to the table... MyTable.Columns.Add(MyColumn);

Make a row to be inserted to the table, which copies the table structure DataRow MyRow = MyTable.NewRow();

Add the row to the table with MyTable.Rows.Add(MyRow);

And return the value of the cell in row 0 of the column MyColumn of the table MyTable with return (double)(MyTable.Rows[0]["MyColumn"]);

Lesson done!!!

Inca answered 16/3, 2012 at 2:56 Comment(0)
G
7

This is right to left execution, so need to use proper parathesis to execute expression

    // 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());
    }
Gash answered 14/3, 2011 at 12:18 Comment(2)
This one works fine, the other similar one doesn't work with 2 digit numbers.Alexalexa
Why should the result of (2.5+10)/5+2.5 be 1.6666? Division over sum takes precedence, right?Slapbang
N
3

You could fairly easily run this through the CSharpCodeProvider with suitable fluff wrapping it (a type and a method, basically). Likewise you could go through VB etc - or JavaScript, as another answer has suggested. I don't know of anything else built into the framework at this point.

I'd expect that .NET 4.0 with its support for dynamic languages may well have better capabilities on this front.

Nonobjective answered 2/12, 2008 at 12:0 Comment(2)
This is also incredibly bad performance.Custodial
Yup. I would hope that the DLR will help on the performance side, at least a bit.Nonobjective
S
3

I recently needed to do this for a project and I ended up using IronPython to do it. You can declare an instance of the engine, and then pass any valid python expression and get the result. If you're just doing simple math expressions, then it would suffice. My code ended up looking similar to:

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

You'd probably not want to create the engine for each expression. You also need a reference to IronPython.dll

Snorkel answered 2/12, 2008 at 15:44 Comment(1)
I just stumbled across something similar -- a javascript engine in .NET, from the same guy that wrote NCalc -- github.com/sebastienros/jintUnguentum
L
3

EDIT: Realised i should really bring the addition and subtraction out seperately aswell to make it a little bit more BODMAS compliant.

Big thanks to Rajesh Jinaga for his Stack based approach. I found it really useful for my needs. The following code is a slight modification of Rajesh's method, which processes divisions first, then multiplications, then finishes up with addition and subtraction. It will also allow the use of booleans in the expressions, where true is treated as 1 and false 0. allowing the use of boolean logic in expressions.

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());
    }

I know there is likely to be a cleaner way of doing it, thought id just share the first look at it in case anyone finds it usefull.

Liquorish answered 11/6, 2011 at 11:34 Comment(3)
watch out: the way the stack grows the division and subtraction should be indexed as list[i + 1] operator list[i - 1]. This code has as answer to the expression ' 5 - 3' = -2 instead of 2Averell
I started from this post in 2017 to make my ExpressionEvaluator. It became a mature library. The code is on github here CodingSeb.ExpressionEvaluatorTriplett
I tried it with 4+3/2 but I was surprised with the result. the result was 4,6666666 WTF?????Slapbang
B
2

Many thanks to Ramesh. I used a version of his simple code to pull a string out a database and use it to do boolean operations in my code.

x is a number like 1500 or 2100 or whatever.

function would be a stored evaluation like x > 1400 and 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  }
Boric answered 21/9, 2012 at 22:55 Comment(0)
G
1

There is not. You will need to use some external library, or write your own parser. If you have the time to do so, I suggest to write your own parser as it is a quite interesting project. Otherwise you will need to use something like bcParser.

Grandsire answered 2/12, 2008 at 12:3 Comment(2)
I know it's interesting, I've done it twice already, one with c and one with c++. I was just wondering if there was a built in possibility with cs/.net as it supports reflection I suspected it was a good possibility.Viridissa
This answer needs to be deleted, because it could be simplified as a comment. Downvoting and flagging for moderator attention.Sealskin
B
-3

Short answer: I don't think so. C# .Net is compiled (to bytecode) and can't evaluate strings at runtime, as far as I know. JScript .Net can, however; but I would still advise you to code a parser and stack-based evaluator yourself.

Bedroom answered 2/12, 2008 at 12:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.