Existe-t-il un évaluateur mathématique de chaîne dans .NET?

93

Si j'ai une chaîne avec une expression mathématique valide telle que:

String s = "1 + 2 * 7";

Existe-t-il une bibliothèque / fonction intégrée dans .NET qui analysera et évaluera cette expression pour moi et retournera le résultat? Dans ce cas 15.

Gars
la source
2
Pas un construit en un. Mais il y en a un assez complet ici .
Strelok
1
Vous pouvez utiliser L'évaluateur d'expression (fonction Eval dans .NET 100% géré)
jadsc
Je viens de créer une solution de code uniquement pour évaluer les expressions mathématiques en C #. Vous pouvez voir le code sur blackbeltcoder.com/Articles/algorithms/ac-expression-evaluator .
Jonathan Wood
Cette bibliothèque semble avoir quelques bogues.
Philippe Lavoie

Réponses:

53

Vous pouvez ajouter une référence à Microsoft Script Control Library (COM) et utiliser un code comme celui-ci pour évaluer une expression. (Fonctionne également pour JScript.)

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

Edit - Version C #.

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

Edit - Le ScriptControl est un objet COM. Dans la boîte de dialogue "Ajouter une référence" du projet, sélectionnez l'onglet "COM" et faites défiler jusqu'à "Microsoft Script Control 1.0" et sélectionnez ok.

user21826
la source
2
Bien que cela soit marqué comme la réponse, c'était il y a 10 ans et COM est un peu mort maintenant. Je préfère la réponse DataTable.Compute ci-dessous.
dwilliss
62

Etrange que cette question célèbre et ancienne n'ait pas de réponse qui suggère le DataTable.Compute"truc" intégré . C'est ici.

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

Les opérateurs arithmétiques suivants sont pris en charge dans les expressions:

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

Plus d'informations: DataColumn.Expression à Expression Syntax .

Tim Schmelter
la source
il y avait une réponse de ma81xx le 15 novembre 11
tatigo
1
Vous avez raison, mais cela n'utilisait pas la méthode Compute.
Tim Schmelter
Votre exemple me donne que System.InvalidCastException n'a pas été géré par le code utilisateur HResult = -2147467262 Comment puis-je résoudre ce problème?
Yuliia Ashomok
Cela fonctionne pour moi, avez-vous utilisé cet exemple de code? Utilisez le débogueur et inspectez la valeur du résultat, le type est mentionné.
Tim Schmelter
28

Pour quiconque développe en C # sur Silverlight, voici une astuce assez intéressante que je viens de découvrir qui permet d'évaluer une expression en appelant le moteur Javascript:

double result = (double) HtmlPage.Window.Eval("15 + 35");
Gars
la source
Je me demande si vous pourriez faire référence à cela ailleurs. Probablement pas, mais ce serait cool.
Joel Coehoorn
4
Comme cela évalue du code Javascript arbitraire, vous voulez probablement être sûr de nettoyer votre entrée et de vous assurer que vous n'affichez pas directement le résultat. (Je pense que ce serait un bon moyen d'introduire XSS sans le savoir)
Dan Esparza
Essayez d'entrer des nombres avec un zéro non significatif, le résultat n'est pas fiable. "054 + 6" vous donne 50 par exemple.
Terry
9
@djerry, c'est parce que les nombres avec un zéro non significatif sont considérés comme octaux par l'évaluateur de JS, et l'octal 054 est égal à la décimale 44.
André Leria
24

Avez-vous vu http://ncalc.codeplex.com ?

Il est extensible, rapide (par exemple, a son propre cache) vous permet de fournir des fonctions personnalisées et des variables au moment de l'exécution en gérant les événements EvaluateFunction / EvaluateParameter. Exemples d'expressions qu'il peut analyser:

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()); 

Il gère également nativement unicode et de nombreux types de données. Il est livré avec un fichier de bois de cerf si vous souhaitez changer de grammaire. Il existe également un fork qui prend en charge MEF pour charger de nouvelles fonctions.

GreyCloud
la source
1
Grande bibliothèque. Également disponible sur NUGET
Paul Grimshaw
C'est ce que j'ai utilisé pour mon solveur d'équations différentielles qui a pris l'entrée d'un utilisateur. La question est ici
kleineg
15

En fait, il y en a une sorte intégrée - vous pouvez utiliser l'espace de noms XPath! Bien que cela nécessite que vous reformatiez la chaîne pour confirmer avec la notation XPath. J'ai utilisé une méthode comme celle-ci pour gérer des expressions 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
la source
12

Au départ, j'ai utilisé le wrapper c # pour muparser . C'était très rapide. La seule solution plus rapide que je connaisse est exprtk . Si vous recherchez d'autres solutions, vous pouvez consulter le benchmark .

Mais dans le cas de .Net, vous pouvez utiliser le support intégré pour compiler le code au moment de l'exécution. L'idée est d'avoir un fichier source "modèle" comme par exemple une ressource intégrée où vous pouvez remplacer la formule pour l'évaluation. Ensuite, vous passez ce code source de classe préparé au compilateur.

Un modèle de base pourrait ressembler à ceci:

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); }
    ...

Notez la% formule% dans laquelle l'expression sera insérée.

Pour compiler, utilisez la classe CSharpCodeProvider. Je ne veux pas mettre ici la source complète. Mais cette réponse pourrait aider:

Après avoir chargé l'assembly en mémoire, vous pouvez créer une instance de votre classe et appeler EvalCode.

schoetbi
la source
9

Encore une autre option maintenant que Roslyn est disponible:

Vous pouvez utiliser la bibliothèque CodeAnalysis.CSharp.Scripting pour cela.

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();
        }
    }
}

paquets 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
la source
2
Cela devrait être la réponse maintenant
sonofaforester
8

Récemment, j'utilisais mXparser, une bibliothèque d'analyseurs mathématiques pour .NET et JAVA. mXparser prend en charge les formules de base ainsi que les formules très sophistiquées / compliquées (y compris les variables, les fonctions, les opérateurs, l'itération et la récursivité).

https://mxparser.codeplex.com/

https://mathparser.org/

Quelques exemples d'utilisation:

Exemple 1:

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

Exemple 2:

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

Exemple 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();

Trouvé récemment - au cas où vous voudriez essayer la syntaxe (et voir le cas d'utilisation avancé), vous pouvez télécharger l' application Scalar Calculator qui est alimentée par mXparser.

Meilleures salutations

Leroy Kegan
la source
C'est la meilleure bibliothèque que j'ai trouvée. Cependant, il ne prend en charge aucun type de données autre que nombre! J'ai besoin de DateTime et String .. Connaissez-vous une bonne alternative?
Homam
5

Si vous avez besoin de quelque chose de très simple, vous pouvez utiliser le 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
la source
3

Un analyseur mathématique simple est assez facile à construire et ne nécessite que quelques lignes de code:

Prenons cet exemple 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);

Pour activer la priorité en mettant entre parenthèses une pile de piles suffira, par exemple archivées par récursivité. Tout ce qui est entre parenthèses est mis sur une nouvelle pile. Enfin, vous pouvez prendre en charge les opérations mathématiques de manière parfaitement lisible par lambdas.

Lorenz Lo Sauer
la source
Vous voudrez peut-être vérifier votre réponse. 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
        }

    }
}
user2069333
la source
3
-1: Il suffit de fusionner ceci dans la réponse @cbp. Il n'y a aucun besoin d'avoir deux réponses qui sont fondamentalement identiques lorsque nous pouvons avoir une réponse fantastique.
Robert MacLean
1

J'ai implémenté un analyseur d'expressions il y a quelques années et j'en avais publié une version dans GitHub et Nuget: Albatross.Expression récemment. Il contient une classe ExecutionContext qui peut évaluer un ensemble d'expressions telles que:

  • MV = Prix * Qté;
  • Prix ​​= (Offre + Demande) / 2;
  • Enchère = 0,6;
  • Demander = 0,8;

Il a également intégré une vérification de référence circulaire qui est utile pour éviter un débordement de pile.

Rushui Guan
la source
1

Vous pouvez utiliser la bibliothèque Math-Expression-Evaluator dont je suis l'auteur. Il prend en charge les expressions simples telles que 2.5+5.9, 17.89-2.47+7.16, 5/2/2+1.5*3+4.58, les expressions avec des parenthèses (((9-6/2)*2-4)/2-6-1)/(2+24/(2+4))et des expressions avec des 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});

Vous pouvez également transmettre des paramètres en tant que variables nommées:

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

Il prend en charge .Net Standard 2.0 et peut donc être utilisé à partir de projets .Net Core ainsi que de .Net Full Framework et il n'a pas de dépendances externes.

Giorgi
la source
0

Évaluateur d'expression léger Flee Fast

https://flee.codeplex.com

Référence de langue

  • ArithmeticOperators Exemple: a * 2 + b ^ 2 - 100% 5
  • Exemple de ComparisonOperators: a <> 100
  • Exemple AndOrXorNotOperators (logique): a> 100 And Not b = 100
  • Exemple ShiftOperators: 100 >> 2
  • Exemple de concaténation: "abc" + "def"
  • Exemple d'indexation: arr [i + 1] + 100
  • Littéraux
  • Exemple de casting: 100 + cast (obj, int)
  • Exemple ConditionalOperator: If (a> 100 et b> 10, "both greater", "less")
  • Exemple InOperator (Liste): If (100 in (100, 200, 300, -1), "in", "not in")
  • Opérateurs surchargés sur les types

Exemple :

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 sortie: 50

ISCI
la source
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));
        }
    }
}

N'oubliez pas de charger

<PackageReference Include="MathNet.Symbolics" Version="0.20.0" />
Wissen Macht Frei
la source