运营商的string

我需要评估在C#中作为string呈现给我的mathexpression式。 例如noddy,但得到了作为expression式的string。

我需要评估然后填充一个int。

在C#中没有Eval()和其他langugaes一样

String myString = "3*4"; 

编辑:

我在VS2008上

试过了Microsoft.JScript。 =它的弃用方法(但仍然符合 – 警告)

然而,我有doens的Microsoft.JScript DLL工作

公共对象InvokeMember(string名称,BindingFlags invokeAttr,活页夹活页夹,对象目标,对象[] args);

抱怨说有一个失踪的“;” 去搞清楚…

编辑2

解决scheme – 是codeDom之一 – 它的工作,因为没有安全问题 – 只有我将永远运行的代码。 非常感谢答复…

而新龙书的链接真棒

编辑3

马特dataTable.Compute()也可以 – 更好的安全意识。 (注明参数检查)

我看到它的方式有两种select – 使用expression式求值器或构造,即时编译和运行C#代码。

我会去一个expression式评估器库,因为你不必担心任何安全问题。 也就是说,您可能无法在中等信任环境(如大多数共享托pipe服务器)中使用代码生成。

下面是生成评估expression式的代码示例: http : //www.vbforums.com/showthread.php?t=397264

所有其他答案都可能矫枉过正。

如果你只需要简单的算术,就这样做。

  DataTable dummy = new DataTable(); Console.WriteLine(dummy.Compute("15 / 3",string.Empty)); 

编辑:多一点信息。 查看System.Data.DataColumn类的Expression属性的MSDN文档。 “expression式语法”中的内容列出了除算术运算符外您可以使用的命令列表。 (例如IIF,LEN等)。 感谢大家投票我的第一个发布的答案!

我几个星期前曾在C#做过这个个人练习。

这是相当多的代码,在地方评论不好。 但它确实与很多testing用例一起工作。

请享用!

 using System; using System.Collections.Generic; using System.Text.RegularExpressions; namespace StackOverflow { class Start { public static void Main(string[] args) { Evaluator ev; string variableValue, eq; Console.Write("Enter equation: "); eq = Console.ReadLine(); while (eq != "quit") { ev = new Evaluator(eq); foreach (Variable v in ev.Variables) { Console.Write(v.Name + " = "); variableValue = Console.ReadLine(); ev.SetVariable(v.Name, Convert.ToDecimal(variableValue)); } Console.WriteLine(ev.Evaluate()); Console.Write("Enter equation: "); eq = Console.ReadLine(); } } } class EvalNode { public virtual decimal Evaluate() { return decimal.Zero; } } class ValueNode : EvalNode { decimal value; public ValueNode(decimal v) { value = v; } public override decimal Evaluate() { return value; } public override string ToString() { return value.ToString(); } } class FunctionNode : EvalNode { EvalNode lhs = new ValueNode(decimal.Zero); EvalNode rhs = new ValueNode(decimal.Zero); string op = "+"; public string Op { get { return op; } set { op = value; } } internal EvalNode Rhs { get { return rhs; } set { rhs = value; } } internal EvalNode Lhs { get { return lhs; } set { lhs = value; } } public override decimal Evaluate() { decimal result = decimal.Zero; switch (op) { case "+": result = lhs.Evaluate() + rhs.Evaluate(); break; case "-": result = lhs.Evaluate() - rhs.Evaluate(); break; case "*": result = lhs.Evaluate() * rhs.Evaluate(); break; case "/": result = lhs.Evaluate() / rhs.Evaluate(); break; case "%": result = lhs.Evaluate() % rhs.Evaluate(); break; case "^": double x = Convert.ToDouble(lhs.Evaluate()); double y = Convert.ToDouble(rhs.Evaluate()); result = Convert.ToDecimal(Math.Pow(x, y)); break; case "!": result = Factorial(lhs.Evaluate()); break; } return result; } private decimal Factorial(decimal factor) { if (factor < 1) return 1; return factor * Factorial(factor - 1); } public override string ToString() { return "(" + lhs.ToString() + " " + op + " " + rhs.ToString() + ")"; } } public class Evaluator { string equation = ""; Dictionary<string, Variable> variables = new Dictionary<string, Variable>(); public string Equation { get { return equation; } set { equation = value; } } public Variable[] Variables { get { return new List<Variable>(variables.Values).ToArray(); } } public void SetVariable(string name, decimal value) { if (variables.ContainsKey(name)) { Variable x = variables[name]; x.Value = value; variables[name] = x; } } public Evaluator(string equation) { this.equation = equation; SetVariables(); } public decimal Evaluate() { return Evaluate(equation, new List<Variable>(variables.Values)); } public decimal Evaluate(string text) { decimal result = decimal.Zero; equation = text; EvalNode parsed; equation = equation.Replace(" ", ""); parsed = Parse(equation, "qx"); if (parsed != null) result = parsed.Evaluate(); return result; } public decimal Evaluate(string text, List<Variable> variables) { foreach (Variable v in variables) { text = text.Replace(v.Name, v.Value.ToString()); } return Evaluate(text); } private static bool EquationHasVariables(string equation) { Regex letters = new Regex(@"[A-Za-z]"); return letters.IsMatch(equation); } private void SetVariables() { Regex letters = new Regex(@"([A-Za-z]+)"); Variable v; foreach (Match m in letters.Matches(equation, 0)) { v = new Variable(m.Groups[1].Value, decimal.Zero); if (!variables.ContainsKey(v.Name)) { variables.Add(v.Name, v); } } } #region Parse V2 private Dictionary<string, string> parenthesesText = new Dictionary<string, string>(); /* * 1. All the text in first-level parentheses is replaced with replaceText plus an index value. * (All nested parentheses are parsed in recursive calls) * 2. The simple function is parsed given the order of operations (reverse priority to * keep the order of operations correct when evaluating). * a. Addition (+), subtraction (-) -> left to right * b. Multiplication (*), division (/), modulo (%) -> left to right * c. Exponents (^) -> right to left * d. Factorials (!) -> left to right * e. No op (number, replaced parentheses) * 3. When an op is found, a two recursive calls are generated -- parsing the LHS and * parsing the RHS. * 4. An EvalNode representing the root node of the evaluations tree is returned. * * Ex. 3 + 5 (3 + 5) * 8 * + * * / \ / \ * 3 5 + 8 * / \ * 3 + 5 * 8 3 5 * + * / \ * 3 * * / \ * 5 8 */ /// <summary> /// Parses the expression and returns the root node of a tree. /// </summary> /// <param name="eq">Equation to be parsed</param> /// <param name="replaceText">Text base that replaces text in parentheses</param> /// <returns></returns> private EvalNode Parse(string eq, string replaceText) { int randomKeyIndex = 0; eq = eq.Replace(" ", ""); if (eq.Length == 0) { return new ValueNode(decimal.Zero); } int leftParentIndex = -1; int rightParentIndex = -1; SetIndexes(eq, ref leftParentIndex, ref rightParentIndex); //remove extraneous outer parentheses while (leftParentIndex == 0 && rightParentIndex == eq.Length - 1) { eq = eq.Substring(1, eq.Length - 2); SetIndexes(eq, ref leftParentIndex, ref rightParentIndex); } //Pull out all expressions in parentheses replaceText = GetNextReplaceText(replaceText, randomKeyIndex); while (leftParentIndex != -1 && rightParentIndex != -1) { //replace the string with a random set of characters, stored extracted text in dictionary keyed on the random set of chars string p = eq.Substring(leftParentIndex, rightParentIndex - leftParentIndex + 1); eq = eq.Replace(p, replaceText); parenthesesText.Add(replaceText, p); leftParentIndex = 0; rightParentIndex = 0; replaceText = replaceText.Remove(replaceText.LastIndexOf(randomKeyIndex.ToString())); randomKeyIndex++; replaceText = GetNextReplaceText(replaceText, randomKeyIndex); SetIndexes(eq, ref leftParentIndex, ref rightParentIndex); } /* * Be sure to implement these operators in the function node class */ char[] ops_order0 = new char[2] { '+', '-' }; char[] ops_order1 = new char[3] { '*', '/', '%' }; char[] ops_order2 = new char[1] { '^' }; char[] ops_order3 = new char[1] { '!' }; /* * In order to evaluate nodes LTR, the right-most node must be the root node * of the tree, which is why we find the last index of LTR ops. The reverse * is the case for RTL ops. */ int order0Index = eq.LastIndexOfAny(ops_order0); if (order0Index > -1) { return CreateFunctionNode(eq, order0Index, replaceText + "0"); } int order1Index = eq.LastIndexOfAny(ops_order1); if (order1Index > -1) { return CreateFunctionNode(eq, order1Index, replaceText + "0"); } int order2Index = eq.IndexOfAny(ops_order2); if (order2Index > -1) { return CreateFunctionNode(eq, order2Index, replaceText + "0"); } int order3Index = eq.LastIndexOfAny(ops_order3); if (order3Index > -1) { return CreateFunctionNode(eq, order3Index, replaceText + "0"); } //no operators... eq = eq.Replace("(", ""); eq = eq.Replace(")", ""); if (char.IsLetter(eq[0])) { return Parse(parenthesesText[eq], replaceText + "0"); } return new ValueNode(decimal.Parse(eq)); } private string GetNextReplaceText(string replaceText, int randomKeyIndex) { while (parenthesesText.ContainsKey(replaceText)) { replaceText = replaceText + randomKeyIndex.ToString(); } return replaceText; } private EvalNode CreateFunctionNode(string eq, int index, string randomKey) { FunctionNode func = new FunctionNode(); func.Op = eq[index].ToString(); func.Lhs = Parse(eq.Substring(0, index), randomKey); func.Rhs = Parse(eq.Substring(index + 1), randomKey); return func; } #endregion /// <summary> /// Find the first set of parentheses /// </summary> /// <param name="eq"></param> /// <param name="leftParentIndex"></param> /// <param name="rightParentIndex"></param> private static void SetIndexes(string eq, ref int leftParentIndex, ref int rightParentIndex) { leftParentIndex = eq.IndexOf('('); rightParentIndex = eq.IndexOf(')'); int tempIndex = eq.IndexOf('(', leftParentIndex + 1); while (tempIndex != -1 && tempIndex < rightParentIndex) { rightParentIndex = eq.IndexOf(')', rightParentIndex + 1); tempIndex = eq.IndexOf('(', tempIndex + 1); } } } public struct Variable { public string Name; public decimal Value; public Variable(string n, decimal v) { Name = n; Value = v; } } } 

你可以使用jscript解释器。 一个伟大的文章是在这里: http : //www.odetocode.com/Articles/80.aspx

当你说,“像在其他语言”,你应该说,“像在dynamic语言”。

对于python,ruby和许多解释语言等dynamic语言,Eval()函数是一个自然元素。 实际上,实现自己的function甚至可能非常微不足道。

无论如何,.Net在核心是一个静态的强types编译平台(至less在Dynamic Language Runtime获得更多支持之前)。 这具有自然的优点,如代码注入安全性和编译时types检查,这些都是难以忽视的。 但是这意味着一个Eval()函数不太适合,它希望能够提前编译expression式。 在这种平台中,通常还有其他更安全的方法来完成相同的任务。

退房逃跑

MS有一个称为dynamic查询库的示例。 它由LINQ团队提供,以dynamic构buildLINQ查询,例如:Dim query = Northwind.Products.Where(“CategoryID = 2”)您可以检查它是否提供了基本的mathfunction。

在解释语言中,您可能有机会使用解释器来评估string。 在C#中,你需要一个语法分析器来写string(mathexpression式的语言)。 这是一个非平凡的练习。 如果你想这样做,使用recursion下降parsing器。 “龙书”(Aho,Sethi和Ullman的编译器:devise等 – 1977年第1版或第2版,2007年)的前几章对你需要做的事情有很好的解释。

另一种方法是在你的项目中join一个用perl编写的组件,这个组件现在应该可以用于.NET,并使用perl来进行评估。

计算expression式时是否需要访问其他variables的值?

jscript解释器可以这样做,或者如果expression式很简单,您可以编写自己的parsing器(注意,它变得非常快)。

我很确定在C#中没有直接的“Eval(string)”方法,因为它没有被解释。

请记住,虽然代码解释是受代码注入,要格外小心:)

经过一些Googlesearch之后,我发现使用CodeDom可以快速创build和编译代码。 (请参阅教程 )。

我个人认为这种方法不是一个好主意,因为用户可以input他想要的任何代码,但这可能是一个需要探索的领域(例如,只validationinput,只允许数字和简单的math运算) 。

其他一些build议:

  • 单声道2.0(今天出来)有一个评价方法。
  • 你可以很容易地编写一个特定的小域名。
  • 您可以创build一个老派的recursion下降EBNF分析器。

我已经在我的网站上发布了超紧凑(1类,<10 KiB)Javamath评估器的源代码。 把它移植到C#应该是微不足道的。 还有其他的可能会做更多,但这是非常有能力的,而且很小