Evaluación de cadena & # 8220; 3 * (4 + 2) & # 8221; rendimiento int 18 [duplicado]
Pregunta
Esta pregunta ya tiene una respuesta aquí:
¿Existe una función del marco .NET que pueda evaluar una expresión numérica contenida en una cadena y devolver el resultado? F.e .:
string mystring = "3*(2+4)";
int result = EvaluateExpression(mystring);
Console.Writeln(result); // Outputs 18
¿Existe una función marco estándar con la que pueda reemplazar mi método EvaluateExpression
?
Solución
Sí, puede dejar que el compilador de C # lo evalúe en tiempo de ejecución.
Ver: CSharpCorner
Otros consejos
Si desea evaluar una expresión de cadena, use el fragmento de código siguiente.
using System.Data;
DataTable dt = new DataTable();
var v = dt.Compute("3 * (2+4)","");
El uso del compilador 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 este único propósito. 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());
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"]);
}
Podrías mirar " 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 ")));
}
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á en la columna, que es igual a poner System.Type.GetType (" System.Double "); en su 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 completará con la '' Expresión '', y acepta prácticamente todo lo 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
.
Luego, se agrega una fila a la tabla con una columna personalizada con un atributo de Expresión, realizada a través de loDataTable.Rows.Add (0);
. Cuando agregamos esta fila, la celda de la columna "Evaluar" de la tabla loDataTable
se completa automáticamente con su " Expression " atributo y, si tiene operadores y consultas SQL, etc., se evalúa y luego se almacena en la celda, por lo tanto, aquí sucede la `` magia '', la cadena con operadores se evalúa y se almacena en una celda ...
Finalmente, simplemente devuelva el valor almacenado en la celda de la columna "Evaluar" en la fila 0 (es un índice, comienza a contar desde cero) y realiza una conversión a doble con return (double) (loDataTable.Rows [0] [" Eval "]);
.
Y eso es todo ... ¡trabajo hecho!
Y aquí hay un código fácil de entender, que hace lo mismo ... No está dentro de un método, y también se explica.
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 su contenido cuando está almacenado en la tabla. Hecho a través de MyColumn.ColumnName = " MyColumn " ;;
Entonces, 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 que se inserte una fila 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 terminada!
Este es un evaluador de expresiones simple que utiliza pilas
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();
}
}
Esta es una ejecución de derecha a izquierda, por lo que debe usar una parátesis 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());
}
Podría ejecutar esto con bastante facilidad a través de CSharpCodeProvider con la envoltura adecuada (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 integrado en el marco en este momento.
Esperaría que .NET 4.0 con su soporte para lenguajes dinámicos pueda tener mejores capacidades en este frente.
Hace poco necesitaba hacer esto para un proyecto y terminé usando IronPython para hacerlo. Puede declarar una instancia del motor y luego pasar cualquier expresión de Python válida y obtener el resultado. Si solo estás haciendo expresiones matemáticas simples, entonces sería suficiente. Mi código terminó pareciéndose 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
EDIT: me di cuenta de que realmente debería sacar la suma y la resta por separado también para que sea un poco más compatible 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 primero las divisiones, luego las multiplicaciones y luego termina con la suma y la resta. También permitirá el uso de booleanos en las expresiones, donde verdadero se trata como 1 y falso 0. permitiendo el uso de lógica booleana en las 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 forma más limpia de hacerlo, aunque pensé que la identificación simplemente compartiría el primer vistazo en caso de que alguien lo encuentre útil.
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 realizar operaciones booleanas en mi código.
x es un número como 1500 o 2100 o lo que sea.
La funciónserí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 }
No hay. Deberá utilizar alguna biblioteca externa o escribir su propio analizador. Si tiene tiempo para hacerlo, le sugiero que escriba su propio analizador, ya que es un proyecto bastante interesante. De lo contrario, deberá usar algo como bcParser .
Respuesta corta: no lo creo. C # .Net está compilado (a bytecode) y, por lo que sé, no puede evaluar cadenas en tiempo de ejecución. JScript .Net puede, sin embargo; pero igual le aconsejaría que codifique un analizador y un evaluador basado en pila usted mismo.