¿Existe un evaluador matemático de cadenas en .NET?

95

Si tengo una cadena con una expresión matemática válida como:

String s = "1 + 2 * 7";

¿Hay una biblioteca / función incorporada en .NET que analizará y evaluará esa expresión por mí y devolverá el resultado? En este caso 15.

Chico
fuente
2
No construido en uno. Pero hay una muy amplia aquí .
Strelok
1
Puede usar el evaluador de expresiones (función de evaluación en .NET 100% administrado)
jadsc
Acabo de crear una solución de solo código para evaluar expresiones matemáticas en C #. Puede ver el código en blackbeltcoder.com/Articles/algorithms/ac-expression-evaluator .
Jonathan Wood
Esta biblioteca parece tener algunos errores.
Philippe Lavoie

Respuestas:

53

Puede agregar una referencia a Microsoft Script Control Library (COM) y usar un código como este para evaluar una expresión. (También funciona para JScript).

Dim sc As New MSScriptControl.ScriptControl()
sc.Language = "VBScript"
Dim expression As String = "1 + 2 * 7"
Dim result As Double = sc.Eval(expression)

Editar : versión C #.

MSScriptControl.ScriptControl sc = new MSScriptControl.ScriptControl();
sc.Language = "VBScript";
string expression = "1 + 2 * 7";
object result = sc.Eval(expression);            
MessageBox.Show(result.ToString());

Editar : ScriptControl es un objeto COM. En el cuadro de diálogo "Agregar referencia" del proyecto, seleccione la pestaña "COM", desplácese hacia abajo hasta "Microsoft Script Control 1.0" y seleccione Aceptar.

user21826
fuente
2
Aunque esto está marcado como la respuesta, fue hace 10 años y COM está un poco muerto ahora. Prefiero la respuesta de DataTable.Compute a continuación.
dwilliss
64

Es extraño que esta famosa y vieja pregunta no tenga una respuesta que sugiera el DataTable.Compute"truco" incorporado . Aquí está.

double result = Convert.ToDouble(new DataTable().Compute("1 + 2 * 7", null));

Los siguientes operadores aritméticos se admiten en expresiones:

+ (addition)
- (subtraction)
* (multiplication)
/ (division)
% (modulus)

Más información: DataColumn.Expressionen Expression Syntax .

Tim Schmelter
fuente
hubo una respuesta de ma81xx el 15 de noviembre de
2011
1
Tiene razón, pero eso no fue usando el método Compute.
Tim Schmelter
Su ejemplo me da System.InvalidCastException no fue manejado por el código de usuario HResult = -2147467262 ¿Cómo puedo solucionarlo?
Yuliia Ashomok
A mí me funciona, ¿ha utilizado este código de muestra? Utilice el depurador e inspeccione el valor del resultado, se menciona el tipo.
Tim Schmelter
28

Para cualquiera que desarrolle en C # en Silverlight, aquí hay un truco bastante bueno que acabo de descubrir que permite la evaluación de una expresión llamando al motor Javascript:

double result = (double) HtmlPage.Window.Eval("15 + 35");
Chico
fuente
Me pregunto si podría hacer referencia a esto en otro lugar. Probablemente no, pero sería genial.
Joel Coehoorn
4
Como esto evalúa el código Javascript arbitrario, probablemente desee asegurarse de desinfectar su entrada y asegurarse de que no está mostrando directamente el resultado. (Creo que esta sería una buena manera de presentar XSS sin darme cuenta)
Dan Esparza
Intente ingresar números con un cero a la izquierda, el resultado no es confiable. "054 + 6" le da 50 por ejemplo.
Terry
9
@djerry, eso se debe a que el evaluador de JS considera los números con un cero a la izquierda, y el octal 054 es igual al decimal 44.
André Leria
24

¿Has visto http://ncalc.codeplex.com ?

Es extensible, rápido (por ejemplo, tiene su propia caché) le permite proporcionar funciones personalizadas y variables en tiempo de ejecución mediante el manejo de eventos EvaluateFunction / EvaluateParameter. Expresiones de ejemplo que puede analizar:

Expression e = new Expression("Round(Pow(Pi, 2) + Pow([Pi2], 2) + X, 2)"); 

  e.Parameters["Pi2"] = new Expression("Pi * Pi"); 
  e.Parameters["X"] = 10; 

  e.EvaluateParameter += delegate(string name, ParameterArgs args) 
    { 
      if (name == "Pi") 
      args.Result = 3.14; 
    }; 

  Debug.Assert(117.07 == e.Evaluate()); 

También maneja unicode y muchos tipos de datos de forma nativa. Viene con un archivo de asta si desea cambiar la gramática. También hay una bifurcación que admite MEF para cargar nuevas funciones.

GreyCloud
fuente
1
Gran biblioteca. También disponible en NUGET
Paul Grimshaw
Esto es lo que usé para mi solucionador de ecuaciones diferenciales que tomó la entrada de un usuario. La pregunta está aquí
kleineg
15

En realidad, hay uno integrado: ¡puedes usar el espacio de nombres XPath! Aunque requiere que vuelva a formatear la cadena para confirmar con la notación XPath. He usado un método como este para manejar expresiones simples:

    public static double Evaluate(string expression)
    {
        var xsltExpression = 
            string.Format("number({0})", 
                new Regex(@"([\+\-\*])").Replace(expression, " ${1} ")
                                        .Replace("/", " div ")
                                        .Replace("%", " mod "));

        return (double)new XPathDocument
            (new StringReader("<r/>"))
                .CreateNavigator()
                .Evaluate(xsltExpression);
    }
cbp
fuente
12

Inicialmente utilicé la envoltura de c # para muparser . Esto fue muy rápido. La única solución más rápida que conozco es exprtk . Si está buscando otras soluciones, puede consultar el punto de referencia .

Pero en el caso de .Net, puede usar el soporte incorporado para compilar código en tiempo de ejecución. La idea es tener un archivo fuente de "plantilla" como, por ejemplo, un recurso incrustado donde se puede reemplazar la fórmula para la evaluación. Luego pasa este código fuente de clase preparado al compilador.

Una plantilla básica podría verse así:

public class CSCodeEvaler
{
    public double EvalCode()
    {
        return last = Convert.ToDouble(%formula%);
    }

    public double last = 0;
    public const double pi = Math.PI;
    public const double e = Math.E;
    public double sin(double value) { return Math.Sin(value); }
    public double cos(double value) { return Math.Cos(value); }
    public double tan(double value) { return Math.Tan(value); }
    ...

Observe el% fórmula% donde se incluirá la expresión.

Para compilar use la clase CSharpCodeProvider. No quiero poner la fuente completa aquí. Pero esta respuesta podría ayudar:

Una vez que haya cargado el ensamblado en memoria, puede crear una instancia de su clase y llamar a EvalCode.

Schoetbi
fuente
9

Otra opción más ahora que Roslyn está disponible:

Puede usar la biblioteca CodeAnalysis.CSharp.Scripting para esto.

using Microsoft.CodeAnalysis.CSharp.Scripting;
using System;

namespace ExpressionParser
{
    class Program
    {
        static void Main(string[] args)
        {
            //Demonstrate evaluating C# code
            var result = CSharpScript.EvaluateAsync("System.DateTime.Now.AddDays(-1) > System.DateTime.Now").Result;
            Console.WriteLine(result.ToString());

            //Demonstrate evaluating simple expressions
            var result2 = CSharpScript.EvaluateAsync(" 5 * 7").Result;
            Console.WriteLine(result2);
            Console.ReadKey();
        }
    }
}

paquetes nuget:

<package id="Microsoft.CodeAnalysis.Analyzers" version="1.1.0" targetFramework="net461" />
<package id="Microsoft.CodeAnalysis.Common" version="1.1.1" targetFramework="net461" />
<package id="Microsoft.CodeAnalysis.CSharp" version="1.1.1" targetFramework="net461" />
<package id="Microsoft.CodeAnalysis.CSharp.Scripting" version="1.1.1" targetFramework="net461" />
<package id="Microsoft.CodeAnalysis.Scripting" version="1.1.1" targetFramework="net461" />
<package id="Microsoft.CodeAnalysis.Scripting.Common" version="1.1.1" targetFramework="net461" />
Crowcoder
fuente
2
Esta debería ser la respuesta ahora
sonofaforester
8

Recientemente estaba usando mXparser, que es una biblioteca de analizador matemático para .NET y JAVA. mXparser admite fórmulas básicas así como fórmulas muy sofisticadas / complicadas (incluidas variables, funciones, operadores, iteración y recursión).

https://mxparser.codeplex.com/

https://mathparser.org/

Algunos ejemplos de uso:

Ejemplo 1:

Expression e = new Expression("1+2*7 + (sin(10) - 2)/3");
double v = e.calculate();

Ejemplo 2:

Argument x = new Argument("x = 5");
Expression e = new Expression("2*x+3", x);
double v = e.calculate();

Ejemplo 3:

Function f = new Function("f(x,y) = sin(x) / cos(y)");
Expression e = new Expression("f(pi, 2*pi) - 2", f);
double v = e.calculate();

Encontrado recientemente: en caso de que desee probar la sintaxis (y ver el caso de uso avanzado), puede descargar la aplicación Scalar Calculator que funciona con mXparser.

Atentamente

Leroy Kegan
fuente
Esta es la mejor biblioteca que encontré. Sin embargo, no admite ningún tipo de datos que no sea el número. Necesito DateTime y String ... ¿conoces alguna buena alternativa?
Homam
@Homam ¿Encontró una alternativa para las operaciones de cuerdas
Ramakrishna Reddy
5

Si necesita algo muy simple, puede usar DataTable:-)

Dim dt As New DataTable
dt.Columns.Add("A", GetType(Integer))
dt.Columns.Add("B", GetType(Integer))
dt.Columns.Add("C", GetType(Integer))
dt.Rows.Add(New Object() {12, 13, DBNull.Value})

Dim boolResult As Boolean = dt.Select("A>B-2").Length > 0

dt.Columns.Add("result", GetType(Integer), "A+B*2+ISNULL(C,0)")
Dim valResult As Object = dt.Rows(0)("result")
ma81xx
fuente
3

Un analizador matemático simple es bastante fácil de construir y solo requiere unas pocas líneas de código:

Tome este ejemplo flexible:

class RPN
{
    public static double Parse( Stack<string> strStk )
    {
        if (strStk == null || strStk.Count == 0 )
        {
            return 0;
        }
        Stack<double> numStk = new Stack<double>();
        double result = 0;

        Func<double, double> op = null;
        while (strStk.Count > 0)
        {
            var s = strStk.Pop();
            switch (s)
            {
                case "+":
                    op = ( b ) => { return numStk.Pop() + b; };
                    break;
                case "-":
                    op = ( b ) => { return numStk.Pop() - b; };
                    break;
                case "*":
                    op = ( b ) => { return numStk.Pop() * b; };
                    break;
                case "/":
                    op = ( b ) => { return numStk.Pop() / b; };
                    break;

                default:
                    double.TryParse(s, NumberStyles.Any, out result);
                    if (numStk.Count > 0)
                    {
                        result = op(result);
                    }
                    numStk.Push(result);
                    break;
            }
        }
        return result;
    }
}

....
var str = " 100.5 + 300.5 - 100 * 10 / 100";    
str = Regex.Replace(str, @"\s", "", RegexOptions.Multiline);
Stack<string> strStk = new Stack<string>(
     Regex.Split(str, @"([()*+\/-])", RegexOptions.Multiline).Reverse()
);
RPN.Parse(strStk);

Para habilitar la precedencia, será suficiente poner entre corchetes una pila de pilas, como archivadas por recursividad. Todo lo que esté entre paréntesis se coloca en una nueva pila. Finalmente, puede admitir operaciones matemáticas de una manera legible y limpia mediante lambdas.

Lorenz Lo Sauer
fuente
Es posible que desee verificar su respuesta. 100.5 + 300.5 - 100 * 10 / 100 = 30.1vs391
Tawani
1
namespace CalcExp
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            double res = Evaluate("4+5/2-1");

            Console.WriteLine(res);

        }

        public static double Evaluate(string expression)
        {
            var xsltExpression =
                string.Format("number({0})",
                    new Regex(@"([\+\-\*])").Replace(expression, " ${1} ")
                                            .Replace("/", " div ")
                                            .Replace("%", " mod "));

// ReSharper disable PossibleNullReferenceException
            return (double)new XPathDocument
                (new StringReader("<r/>"))
                    .CreateNavigator()
                    .Evaluate(xsltExpression);
// ReSharper restore PossibleNullReferenceException
        }

    }
}
usuario2069333
fuente
3
-1: Simplemente combine esto en la respuesta @cbp. Hay CERO necesidad de tener dos respuestas que son fundamentalmente idénticas cuando podemos tener una respuesta fantástica.
Robert MacLean
1

Implementé un analizador de expresiones hace unos años y había publicado una versión en GitHub y Nuget: Albatross.Expression recientemente. Contiene una clase ExecutionContext que puede evaluar un conjunto de expresiones como:

  • MV = Precio * Cant;
  • Precio = (oferta + demanda) / 2;
  • Oferta = .6;
  • Preguntar = .8;

También tiene una verificación de referencia circular incorporada que es útil para evitar un desbordamiento de la pila.

Rushui Guan
fuente
1

Puede usar la biblioteca Math-Expression-Evaluator de la que soy autor. Es compatible con expresiones simples, tales como 2.5+5.9, 17.89-2.47+7.16, 5/2/2+1.5*3+4.58, expresiones con paréntesis (((9-6/2)*2-4)/2-6-1)/(2+24/(2+4))y expresiones con variables:

var a = 6;
var b = 4.32m;
var c = 24.15m;
var engine = new ExpressionEvaluator();
engine.Evaluate("(((9-a/2)*2-b)/2-a-1)/(2+c/(2+4))", new { a, b, c});

También puede pasar parámetros como variables con nombre:

dynamic dynamicEngine = new ExpressionEvaluator();

var a = 6;
var b = 4.5m;
var c = 2.6m;

dynamicEngine.Evaluate("(c+b)*a", a: 6, b: 4.5, c: 2.6);

Es compatible con .Net Standard 2.0, por lo que se puede utilizar desde .Net Core así como desde proyectos de .Net Full Framework y no tiene dependencias externas.

Giorgi
fuente
0

Evaluador de expresiones ligeras Flee Fast

https://flee.codeplex.com

Referencia idiomática

  • Operadores aritméticos Ejemplo: a * 2 + b ^ 2-100% 5
  • ComparisonOperators Ejemplo: a <> 100
  • Ejemplo de AndOrXorNotOperators (lógico): a> 100 And Not b = 100
  • Ejemplo de ShiftOperators: 100 >> 2
  • Ejemplo de concatenación: "abc" + "def"
  • Ejemplo de indexación: arr [i + 1] + 100
  • Literales
  • Ejemplo de lanzamiento: 100 + cast (obj, int)
  • Ejemplo de operador condicional: si (a> 100 yb> 10, "ambos mayores", "menos")
  • Ejemplo de InOperator (Lista): If (100 in (100, 200, 300, -1), "in", "not in")
  • Operadores sobrecargados en tipos

Ejemplo:

Imports Ciloci.Flee
Imports Ciloci.Flee.CalcEngine
Imports System.Math

    Dim ec As New Ciloci.Flee.ExpressionContext
    Dim ex As IDynamicExpression
    ec.Imports.AddType(GetType(Math))

    ec.Variables("a") = 10            
    ec.Variables("b") = 40               
    ex = ec.CompileDynamic("a+b")

    Dim evalData    
    evalData = ex.Evaluate()
    Console.WriteLine(evalData)

La salida: 50

ISCI
fuente
0

MathNet.Symbolics

using System;
using static MathNet.Symbolics.SymbolicExpression;
using static System.Console;
using static System.Numerics.Complex;
using Complex = System.Numerics.Complex;

namespace MathEvaluator
{
    class Program
    {
        static readonly Complex i = ImaginaryOne;

        static void Main(string[] args)
        {
            var z = Variable("z");
            Func<Complex, Complex> f = Parse("z * z").CompileComplex(nameof(z));
            Complex c = 1 / 2 - i / 3;
            WriteLine(f(c));


            var x = Variable("x");
            Func<double, double> g = Parse("x * x + 5 * x + 6").Compile(nameof(x));
            double a = 1 / 3.0;
            WriteLine(g(a));
        }
    }
}

No olvides cargar

<PackageReference Include="MathNet.Symbolics" Version="0.20.0" />
Programador orientado al dinero
fuente