评估string“3 *(4 + 2)”收益int 18
有没有一个函数的.NET框架,可以评估包含在string中的数字expression式并返回结果? 铁:
string mystring = "3*(2+4)"; int result = EvaluateExpression(mystring); Console.Writeln(result); // Outputs 18
有一个标准的框架function,你可以用我的EvaluateExpression
方法replace?
是的,您可以让C#编译器在运行时对其进行评估。
请参阅: CSharpCorner
如果你想评估一个stringexpression式使用下面的代码片段。
using System.Data; DataTable dt = new DataTable(); var v = dt.Compute("3 * (2+4)","");
使用编译器来执行意味着内存泄漏,因为生成的程序集被加载并且从不被释放。 与使用真正的expression式解释器相比,性能也不高。 为此,您可以使用Ncalc ,这是一个开放源代码框架,完全是为了这个目的。 你也可以定义自己的variables和自定义函数,如果已经包含的variables和自定义函数是不够的。
例:
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的mathexpression式,它对我来说工作正常。
这里是我用于我的程序的代码:
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();
创build一个表格var loDataTable = new DataTable();
就像在数据库引擎(例如MS SQL)中一样。
然后,用一些特定的参数( var loDataColumn = new DataColumn("Eval", typeof (double), expression);
)创build一个列。
"Eval"
参数是列的名称(ColumnName属性)。
typeof (double)
是要存储在列中的数据的types,它等于把System.Type.GetType("System.Double");
代替。
expression
是Evaluate
方法接收的string,存储在列的属性Expression
中。 这个属性是为了一个真正特定的目的(显而易见),这就是列上的每一行都将被填充“expression式”,并且实际上可以接受一个查询语句。 请参阅http://msdn.microsoft.com/en-us/library/system.data.datacolumn.expression(v=vs.100).aspx以了解可以放在Expression属性中的内容以及如何评估它。
然后, loDataTable.Columns.Add(loDataColumn);
将loDataColumn
列loDataColumn
到loDataTable
表中。
然后,通过loDataTable.Rows.Add(0);
完成将行添加到具有Expression属性的个性化列的表中loDataTable.Rows.Add(0);
。 当我们添加这行时,表loDataTable
的列“Eval”的单元格会自动用其“Expression”属性loDataTable
,如果它有运算符和SQL查询等, loDataTable
评估并存储到单元格中,这里发生的“魔术”,string与运营商评估和存储到一个单元格…
最后,只要返回存储在第0行的列“Eval”的单元格中的值(它是一个索引,从零开始计数),并使用return (double) (loDataTable.Rows[0]["Eval"]);
。
这就是所有工作完成!
在这里,一个代码eaiser要了解,这是相同的……这不是在一个方法,它也解释了。
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();
创build表DataTable MyTable = new DataTable();
然后, DataColumn MyColumn = new DataColumn();
接下来,我们给该列添加一个名称。 这样我们可以在存储到表中时search它的内容。 通过MyColumn.ColumnName = "MyColumn";
然后,expression式,这里我们可以把一个stringtypes的variables,在这种情况下,有一个预定义的string“5 + 5/5”,结果是6。
要存储到列的数据typesMyColumn.DataType = typeof(double);
将列添加到表… MyTable.Columns.Add(MyColumn);
将行插入到表中,该表复制表结构DataRow MyRow = MyTable.NewRow();
使用MyTable.Rows.Add(MyRow);
将该行添加到表中MyTable.Rows.Add(MyRow);
return (double)(MyTable.Rows[0]["MyColumn"]);
表MyTable
MyColumn
列的第0行的单元格的值return (double)(MyTable.Rows[0]["MyColumn"]);
完成的课程!
这是一个使用Stacks的简单的Expression Evaluator
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(); } }
你可以很容易地通过CSharpCodeProvider运行这个合适的绒毛包装它(一种types和一种方法,基本上)。 同样,你可以通过VB等 – 或JavaScript,如另一个答案已经build议。 在这一点上,我不知道有什么其他内置的东西。
我期望.NET 4.0支持dynamic语言,在这方面可能会有更好的function。
我最近需要为一个项目做这个,而我最终使用IronPython来做到这一点。 您可以声明引擎的一个实例,然后传递任何有效的pythonexpression式并获得结果。 如果你只是做简单的mathexpression式,那就足够了。 我的代码最终看起来类似于:
IronPython.Hosting.PythonEngine pythonEngine = new IronPython.Hosting.PythonEngine(); string expression = "3*(2+4)"; double result = pythonEngine.EvaluateAs<double>(expression);
您可能不想为每个expression式创build引擎。 您还需要对IronPython.dll的引用
这是左执行的权利,所以需要使用适当的parathesis来执行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()); }
编辑:意识到我应该真的把加法和减法出来,以使它更多一点BODMAS兼容。
非常感谢Rajesh Jinaga基于Stack的方法。 我发现它对我的需求非常有用。 下面的代码是对Rajesh方法的一个小修改,Rajesh的方法首先处理分割,然后是乘法,然后完成加法和减法。 它也将允许在expression式中使用布尔值,其中true被视为1,false为0.允许在expression式中使用布尔逻辑。
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。 我用他的简单代码的一个版本来拉出一个数据库的string,并使用它在我的代码中执行布尔操作。
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 }
那没有。 你将需要使用一些外部库,或者编写你自己的parsing器。 如果您有时间这样做,我build议编写自己的parsing器,因为这是一个非常有趣的项目。 否则,你将需要使用像bcParser 。
简短的回答:我不这么认为。 C#.Net被编译(字节码),并且不能在运行时计算string,据我所知。 然而,JScript .Net可以; 但我仍然build议你自己编写一个parsing器和基于堆栈的评估器。