112

.NET 框架中是否有一个函数可以计算字符串中包含的数值表达式并返回结果?铁:

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

是否有一个标准的框架函数可以替换我的EvaluateExpression方法?

4

13 回答 13

182

如果要评估字符串表达式,请使用以下代码片段。

using System.Data;

DataTable dt = new DataTable();
var v = dt.Compute("3 * (2+4)","");
于 2012-06-14T09:03:13.200 回答
84

使用编译器来做意味着内存泄漏,因为生成的程序集被加载并且从未释放。它的性能也低于使用真正的表达式解释器。为此,您可以使用Ncalc,它是一个仅用于此目的的开源框架。如果已经包含的变量和自定义函数还不够,您还可以定义自己的变量和自定义函数。

例子:

Expression e = new Expression("2 + 3 * 5");
Debug.Assert(17 == e.Evaluate());
于 2009-10-14T07:47:01.253 回答
51

尝试这个:

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"]);
}
于 2009-09-13T11:13:41.233 回答
15

你可以看看“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 ")));
}
于 2009-09-13T10:47:48.460 回答
14
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"]); 
} 

解释它是如何工作的:

首先,我们在 part 中创建一个表var loDataTable = new DataTable();,就像在数据库引擎中一样(例如 MS SQL)。

然后是一列,带有一些特定参数 ( var loDataColumn = new DataColumn("Eval", typeof (double), expression);)。

"Eval"参数是列的名称(ColumnName 属性)。

typeof (double)System.Type.GetType("System.Double");是要存储在列中的数据类型,取而代之等于put 。

expressionEvaluate方法接收的字符串,存储在Expression列的属性中。此属性用于一个非常特定的目的(很明显),即放在列上的每一行都将用“表达式”填充,并且它实际上接受可以放在 SQL 查询中的任何内容。请参阅http://msdn.microsoft.com/en-us/library/system.data.datacolumn.expression(v=vs.100).aspx以了解可以在 Expression 属性中放入什么以及如何评估它。

然后,loDataTable.Columns.Add(loDataColumn);将该列添加loDataColumnloDataTable表中。

然后,将一行添加到表中,其中包含具有 Expression 属性的个性化列,通过loDataTable.Rows.Add(0);. 当我们添加这一行时,表格的“Eval”列的单元格loDataTable会自动填充其“Expression”属性,如果它有运算符和SQL查询等,它会被评估然后存储到单元格中,所以,这里发生了“魔术”,带有运算符的字符串被评估并存储到一个单元格中......

最后,只需将存储的值返回到第 0 行中“Eval”列的单元格(它是一个索引,从零开始计数),并使用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);

MyColumn并返回表格列的第 0 行单元格的MyTablereturn (double)(MyTable.Rows[0]["MyColumn"]);

教训完了!!!

于 2012-03-16T02:56:13.423 回答
14

这是一个使用 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();
    }
}
于 2010-11-16T14:54:20.537 回答
7

这是从右到左执行,所以需要使用适当的括号来执行表达式

    // 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());
    }
于 2011-03-14T12:18:48.637 回答
4

编辑:意识到我真的应该把加法和减法分别拿出来,让它更符合 BODMAS 标准。

非常感谢 Rajesh Jinaga 的基于 Stack 的方法。我发现它对我的需求非常有用。下面的代码是对 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());
    }

我知道可能有一种更清洁的方式来做这件事,我想我只是分享一下它的第一眼,以防有人发现它有用。

于 2011-06-11T11:34:16.513 回答
3

您可以相当容易地通过 CSharpCodeProvider 运行它,并使用合适的绒毛包装它(基本上是一种类型和一种方法)。同样,您可以通过 VB 等 - 或 JavaScript,正如另一个答案所建议的那样。在这一点上,我不知道框架中内置的任何其他内容。

我希望.NET 4.0 支持动态语言在这方面可能有更好的能力。

于 2008-12-02T12:00:48.570 回答
3

我最近需要为一个项目做这件事,我最终使用IronPython来做这件事。您可以声明引擎的一个实例,然后传递任何有效的 python 表达式并获取结果。如果您只是在做简单的数学表达式,那就足够了。我的代码最终看起来类似于:

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

您可能不想为每个表达式创建引擎。您还需要对 IronPython.dll 的引用

于 2008-12-02T15:44:26.340 回答
2

非常感谢拉梅什。我使用他的简单代码的一个版本从数据库中提取一个字符串,并使用它在我的代码中执行布尔运算。

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  }
于 2012-09-21T22:55:41.610 回答
1

那没有。您将需要使用一些外部库,或编写自己的解析器。如果您有时间这样做,我建议您编写自己的解析器,因为这是一个非常有趣的项目。否则,您将需要使用bcParser 之类的东西。

于 2008-12-02T12:03:45.787 回答
-3

简短的回答:我不这么认为。据我所知,C# .Net 已编译(转换为字节码)并且无法在运行时评估字符串。但是,JScript .Net 可以;但我仍然建议您自己编写解析器和基于堆栈的评估器。

于 2008-12-02T12:02:03.853 回答