c# - ncalc - La cadena de evaluación "3*(4+2)" produce int 18
c# eval string expression (14)
No hay. Tendrá que usar alguna biblioteca externa, o escribir su propio analizador. Si tienes tiempo para hacerlo, te sugiero que escribas tu propio analizador ya que es un proyecto bastante interesante. De lo contrario, necesitarás usar algo como bcParser .
Esta pregunta ya tiene una respuesta aquí:
¿Existe una función del framework .NET que pueda evaluar una expresión numérica contenida en una cadena y devolver el resultado? Fe:
string mystring = "3*(2+4)";
int result = EvaluateExpression(mystring);
Console.Writeln(result); // Outputs 18
¿Existe una función de marco estándar con la que pueda reemplazar mi método EvaluateExpression
?
Respuesta corta: no lo creo C # .Net está compilado (a bytecode) y no puedo evaluar cadenas en tiempo de ejecución, hasta donde yo sé. JScript .Net puede, sin embargo; pero aún así te aconsejaría que codificaras un analizador sintáctico y un analizador basado en la pila.
Sí, puedes dejar que el compilador de C # lo evalúe en tiempo de ejecución.
Ver: CSharpCorner
Fácilmente podría ejecutar esto a través del CSharpCodeProvider con la envoltura adecuada de pelusa (un tipo y un método, básicamente). Del mismo modo, podría pasar por VB, etc., o JavaScript, como ha sugerido otra respuesta. No sé nada más incorporado en el marco en este momento.
Espero que .NET 4.0 con su soporte para lenguajes dinámicos pueda tener mejores capacidades en este frente.
Recientemente tuve que hacer esto para un proyecto y terminé usando IronPython para hacerlo. Puede declarar una instancia del motor y luego pasar cualquier expresión válida de python y obtener el resultado. Si solo estás haciendo expresiones matemáticas simples, entonces sería suficiente. Mi código terminó pareciendo similar a:
IronPython.Hosting.PythonEngine pythonEngine = new IronPython.Hosting.PythonEngine();
string expression = "3*(2+4)";
double result = pythonEngine.EvaluateAs<double>(expression);
Probablemente no quieras crear el motor para cada expresión. También necesita una referencia a IronPython.dll
Prueba esto:
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"]);
}
Puedes ver "XpathNavigator.Evaluate". He usado esto para procesar expresiones matemáticas para mi GridView y funciona bien para mí.
Aquí está el código que utilicé para mi programa:
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 ")));
}
Usar el compilador para hacer implica pérdidas de memoria a medida que los ensamblados generados se cargan y nunca se liberan. También es menos eficiente que usar un intérprete de expresión real. Para este propósito, puede usar Ncalc, que es un marco de código abierto con esta única intención. También puede definir sus propias variables y funciones personalizadas si las que ya están incluidas no son suficientes.
Ejemplo:
Expression e = new Expression("2 + 3 * 5");
Debug.Assert(17 == e.Evaluate());
Esta es la ejecución de derecha a izquierda, por lo que es necesario usar la paratesis adecuada para ejecutar la expresión
// 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());
}
EDITAR: Me di cuenta de que realmente debería sacar las sumas y restas por separado para que sean un poco más compatibles con BODMAS.
Muchas gracias a Rajesh Jinaga por su enfoque basado en Stack. Lo encontré realmente útil para mis necesidades. El siguiente código es una ligera modificación del método de Rajesh, que procesa divisiones primero, luego multiplicaciones, luego termina con suma y resta. También permitirá el uso de booleanos en las expresiones, donde true se trata como 1 y falso 0. lo que permite el uso de lógica booleana en expresiones.
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());
}
Sé que es probable que haya una manera más clara de hacerlo, pensó ID simplemente compartir la primera mirada en caso de que alguien lo encuentre útil.
Si desea evaluar una expresión de cadena, use el siguiente fragmento de código.
using System.Data;
DataTable dt = new DataTable();
var v = dt.Compute("3 * (2+4)","");
Muchas gracias a Ramesh. Usé una versión de su código simple para extraer una cadena de una base de datos y usarla para hacer operaciones booleanas en mi código.
x es un número como 1500 o 2100 o lo que sea.
la función sería una evaluación almacenada como x> 1400 y 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 }
Este es un Evaluador de Expresiones simple usando 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();
}
}
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"]);
}
Explicación de cómo funciona:
Primero, hacemos una tabla en la parte var loDataTable = new DataTable();
, al igual que en un motor de base de datos (MS SQL, por ejemplo).
Luego, una columna, con algunos parámetros específicos ( var loDataColumn = new DataColumn("Eval", typeof (double), expression);
).
El parámetro "Eval"
es el nombre de la columna (atributo ColumnName).
typeof (double)
es el tipo de datos que se almacenarán en la columna, que es igual a poner System.Type.GetType("System.Double");
en lugar.
expression
es la cadena que recibe el método Evaluate
, y se almacena en el atributo Expression
de la columna. Este atributo es para un propósito realmente específico (obvio), que es que cada fila que se coloca en la columna se llenará con la "Expresión", y acepta prácticamente wathever que se puede poner en una consulta SQL. Consulte http://msdn.microsoft.com/en-us/library/system.data.datacolumn.expression(v=vs.100).aspx para saber qué se puede poner en el atributo Expression y cómo se evalúa.
Luego, loDataTable.Columns.Add(loDataColumn);
agrega la columna loDataColumn
a la tabla loDataTable
.
A continuación, se agrega una fila a la tabla con una columna personalizada con un atributo Expresión, realizada a través de loDataTable.Rows.Add(0);
. Cuando agregamos esta fila, la celda de la columna "Eval" de la tabla loDataTable
se loDataTable
automáticamente con su atributo "Expression" y, si tiene operadores y consultas SQL, etc., se evalúa y luego se almacena en la celda, por lo que , aquí sucede la "magia", la cadena con operadores se evalúa y almacena en una celda ...
Finalmente, simplemente devuelva el valor almacenado en la celda de la columna "Eval" en la fila 0 (es un índice, comienza a contar desde cero) y realice una conversión a un doble con return (double) (loDataTable.Rows[0]["Eval"]);
.
Y eso es todo ... ¡trabajo hecho!
Y aquí un código más fácil de entender, que hace lo mismo ... No está dentro de un método, y se explica también.
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"]);
Primero, cree la tabla con DataTable MyTable = new DataTable();
Luego, una columna con DataColumn MyColumn = new DataColumn();
A continuación, ponemos un nombre a la columna. Esto para que podamos buscar en su contenido cuando se almacena en la mesa. Hecho a través de MyColumn.ColumnName = "MyColumn";
Luego, la Expresión, aquí podemos poner una variable de tipo cadena, en este caso hay una cadena predefinida "5 + 5/5", cuyo resultado es 6.
El tipo de datos que se almacenarán en la columna MyColumn.DataType = typeof(double);
Agregue la columna a la tabla ... MyTable.Columns.Add(MyColumn);
Haga una fila para insertar en la tabla, que copia la estructura de la tabla DataRow MyRow = MyTable.NewRow();
Agregue la fila a la tabla con MyTable.Rows.Add(MyRow);
Y devuelve el valor de la celda en la fila 0 de la columna MyColumn
de la tabla MyTable
con return (double)(MyTable.Rows[0]["MyColumn"]);
Lección hecha!