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 ?

¿Fue útil?

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ó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  }

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.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top