Cadeia de avaliação “3 * (4 + 2)” yield int 18

Existe uma function do .NET framework que pode avaliar uma expressão numérica contida em uma string e retornar o resultado? Fe:

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

Existe uma function de estrutura padrão que você pode replace meu método EvaluateExpression com?

Sim, você pode deixar o compilador C # avaliá-lo em tempo de execução.

Veja: CSharpCorner

Se você quiser avaliar uma expressão de string, use o trecho de código abaixo.

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

Usar o compilador para fazer implica vazamentos de memory como os assemblies gerados são carregados e nunca liberados. Também é menos eficiente do que usar um intérprete de expressão real. Para esse propósito, você pode usar o Ncalc, que é uma estrutura de código aberto com essa única intenção. Você também pode definir suas próprias variables ​​e funções personalizadas se as já incluídas não forem suficientes.

Exemplo:

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

Tente isto:

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

Você poderia olhar para “XpathNavigator.Evaluate” Eu usei isso para processar expressões matemáticas para o meu GridView e funciona bem para mim.

Aqui está o código que usei para o meu programa:

 public static double Evaluate(string expression) { return (double)new System.Xml.XPath.XPathDocument (new StringReader("")).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"]); } 

Explicação de como funciona:

Primeiro, criamos uma tabela na parte var loDataTable = new DataTable(); , assim como em um mecanismo de database (MS SQL, por exemplo).

Então, uma coluna, com alguns parâmetros específicos ( var loDataColumn = new DataColumn("Eval", typeof (double), expression); ).

O parâmetro "Eval" é o nome da coluna (atributo ColumnName).

typeof (double) é o tipo de dado a ser armazenado na coluna, que é igual a colocar System.Type.GetType("System.Double"); em vez de.

expression é a string que o método Evaluate recebe e é armazenada no atributo Expression da coluna. Este atributo é para um propósito realmente específico (óbvio), que é que toda linha que é colocada na coluna será preenchida com a “Expressão”, e aceita praticamente qualquer coisa que possa ser colocada em uma Consulta SQL. Consulte http://msdn.microsoft.com/en-us/library/system.data.datacolumn.expression(v=vs.100).aspx para saber o que pode ser colocado no atributo Expression e como ele é avaliado.

Então, loDataTable.Columns.Add(loDataColumn); adiciona a coluna loDataColumn à tabela loDataTable .

Em seguida, uma linha é adicionada à tabela com uma coluna personalizada com um atributo Expression, feita via loDataTable.Rows.Add(0); . Quando adicionamos essa linha, a célula da coluna “Eval” da tabela loDataTable é preenchida automaticamente com seu atributo “Expression” e, se tiver operadores e consultas SQL, etc., ela é avaliada e, em seguida, armazenada na célula. , aqui acontece a “mágica”, a string com operadores é avaliada e armazenada em uma célula …

Finalmente, basta retornar o valor armazenado para a célula da coluna “Eval” na linha 0 (é um índice, começa a contar a partir de zero), e fazendo uma conversão para um duplo com return (double) (loDataTable.Rows[0]["Eval"]); .

E isso é tudo … trabalho feito!

E aqui um comedor de código para entender, que faz o mesmo … Não está dentro de um método, e é explicado também.

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

Primeiro, crie a tabela com DataTable MyTable = new DataTable();

Em seguida, uma coluna com DataColumn MyColumn = new DataColumn();

Em seguida, colocamos um nome na coluna. Isso nos permite pesquisar em seu conteúdo quando ele é armazenado na tabela. Feito através de MyColumn.ColumnName = "MyColumn";

Então, o Expression, aqui podemos colocar uma variável do tipo string, nesse caso existe uma string pré-definida “5 + 5/5”, cujo resultado é 6.

O tipo de dados a serem armazenados na coluna MyColumn.DataType = typeof(double);

Adicione a coluna à tabela … MyTable.Columns.Add(MyColumn);

Faça uma linha para ser inserida na tabela, que copia a estrutura da tabela DataRow MyRow = MyTable.NewRow();

Adicione a linha à tabela com MyTable.Rows.Add(MyRow);

E retornar o valor da célula na linha 0 da coluna MyColumn da tabela MyTable com return (double)(MyTable.Rows[0]["MyColumn"]);

Lição feita !!!

Este é um simples avaliador de expressões usando pilhas

 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 ops = new Stack(); Stack vals = new Stack(); 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(); } } 

Isso é certo para a execução à esquerda, por isso precisa usar parathesis adequada para executar a expressão

  // 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 stack = new Stack(); 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()); } 

Você poderia facilmente executar isso através do CSharpCodeProvider com um fluff adequado (basicamente um tipo e um método). Da mesma forma você pode passar por VB etc – ou JavaScript, como outra resposta sugeriu. Eu não sei de mais nada construído no framework neste momento.

Eu esperaria que o .NET 4.0, com seu suporte para linguagens dinâmicas, pudesse ter melhores resources nessa frente.

Recentemente, precisei fazer isso para um projeto e acabei usando o IronPython para fazer isso. Você pode declarar uma instância do mecanismo e, em seguida, transmitir qualquer expressão python válida e obter o resultado. Se você está apenas fazendo expressões matemáticas simples, então seria suficiente. Meu código acabou parecido com:

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

Você provavelmente não deseja criar o mecanismo para cada expressão. Você também precisa de uma referência para IronPython.dll

EDIT: Realized eu deveria realmente trazer a adição e subtração separadamente, além de torná-lo um pouco mais BODMAS complacente.

Muito obrigado a Rajesh Jinaga por sua abordagem baseada em Stack. Eu achei muito útil para minhas necessidades. O código a seguir é uma pequena modificação do método de Rajesh, que processa as divisões primeiro, depois as multiplicações e, em seguida, finaliza com adição e subtração. Ele também permitirá o uso de booleanos nas expressões, onde true é tratado como 1 e false 0. permitindo o uso de lógica booleana em expressões.

 public static double Evaluate(string expr) { expr = expr.ToLower(); expr = expr.Replace(" ", ""); expr = expr.Replace("true", "1"); expr = expr.Replace("false", "0"); Stack stack = new Stack(); 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 list = stack.ToList(); 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()); } 

Eu sei que é provável que haja uma maneira mais limpa de fazê-lo, mas apenas compartilhe o primeiro olhar para o caso de alguém achar útil.

Muito obrigado a Ramesh. Eu usei uma versão de seu código simples para puxar uma string de um database e usá-lo para fazer operações booleanas no meu código.

x é um número como 1500 ou 2100 ou qualquer outra coisa.

function seria uma avaliação armazenada como x> 1400 e 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 } 

Não há. Você precisará usar alguma biblioteca externa ou escrever seu próprio analisador. Se você tiver tempo para fazê-lo, sugiro escrever seu próprio analisador, pois é um projeto bastante interessante. Caso contrário, você precisará usar algo como bcParser .

Resposta curta: eu não penso assim. C # .Net é compilado (para bytecode) e não é possível avaliar seqüências de caracteres em tempo de execução, tanto quanto eu sei. JScript .Net pode, no entanto; mas eu ainda aconselho você a codificar um analisador baseado em pilha e analisador você mesmo.