Gibt es in .NET einen String Math Evaluator?

92

Wenn ich eine Zeichenfolge mit einem gültigen mathematischen Ausdruck habe, wie z.

String s = "1 + 2 * 7";

Gibt es in .NET eine integrierte Bibliothek / Funktion, die diesen Ausdruck für mich analysiert und auswertet und das Ergebnis zurückgibt? In diesem Fall 15.

Kerl
quelle
2
Kein eingebauter. Aber es ist ein ziemlich umfassend man hier .
Strelok
1
Sie können den Ausdrucksauswerter (Eval-Funktion in 100% verwaltetem .NET) verwenden
jadsc
Ich habe gerade eine Nur-Code-Lösung zum Auswerten mathematischer Ausdrücke in C # erstellt. Sie können den Code unter blackbeltcoder.com/Articles/algorithms/ac-expression-evaluator sehen .
Jonathan Wood
Diese Bibliothek scheint einige Fehler zu haben.
Philippe Lavoie

Antworten:

53

Sie können einen Verweis auf die Microsoft Script Control Library (COM) hinzufügen und Code wie diesen verwenden, um einen Ausdruck auszuwerten. (Funktioniert auch für JScript.)

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

Bearbeiten - C # -Version.

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

Bearbeiten - Das ScriptControl ist ein COM-Objekt. Wählen Sie im Dialogfeld "Referenz hinzufügen" des Projekts die Registerkarte "COM", scrollen Sie nach unten zu "Microsoft Script Control 1.0" und wählen Sie "OK".

user21826
quelle
2
Obwohl dies als Antwort markiert ist, war es vor 10 Jahren und COM ist jetzt irgendwie tot. Ich bevorzuge die DataTable.Compute-Antwort unten.
Willis
62

Seltsam, dass diese berühmte und alte Frage keine Antwort hat, die den eingebauten DataTable.Compute"Trick" nahe legt . Hier ist es.

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

Die folgenden arithmetischen Operatoren werden in Ausdrücken unterstützt:

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

Weitere Informationen: DataColumn.Expressionunter Ausdruckssyntax .

Tim Schmelter
quelle
Es gab eine Antwort von ma81xx am 15. November 11
Tatigo
1
Sie haben Recht, aber das war nicht die Compute-Methode.
Tim Schmelter
Ihr Beispiel gibt mir System.InvalidCastException wurde vom Benutzercode HResult = -2147467262 nicht behandelt. Wie kann ich das beheben?
Yuliia Ashomok
Es funktioniert bei mir. Haben Sie diesen Beispielcode verwendet? Verwenden Sie den Debugger und überprüfen Sie den Ergebniswert. Der Typ wird erwähnt.
Tim Schmelter
28

Für alle, die in C # auf Silverlight entwickeln, ist hier ein hübscher Trick, den ich gerade entdeckt habe und der die Auswertung eines Ausdrucks durch Aufrufen der Javascript-Engine ermöglicht:

double result = (double) HtmlPage.Window.Eval("15 + 35");
Kerl
quelle
Ich frage mich, ob Sie dies anderswo verweisen könnten. Wahrscheinlich nicht, aber es wäre cool.
Joel Coehoorn
4
Da dies beliebigen Javascript-Code auswertet, möchten Sie wahrscheinlich sicherstellen, dass Ihre Eingabe bereinigt wird und dass das Ergebnis nicht direkt angezeigt wird. (Ich würde denken, dies wäre ein guter Weg, um XSS einzuführen, ohne es zu merken)
Dan Esparza
Versuchen Sie, Zahlen mit einer führenden Null einzugeben. Das Ergebnis ist nicht zuverlässig. "054 + 6" gibt Ihnen zum Beispiel 50.
Terry
9
@djerry, das liegt daran, dass Zahlen mit einer führenden Null vom JS-Bewerter als oktal betrachtet werden und oktal 054 gleich dezimal 44 ist.
André Leria
24

Haben Sie http://ncalc.codeplex.com gesehen ?

Es ist erweiterbar, schnell (z. B. hat es einen eigenen Cache) und ermöglicht es Ihnen, zur Laufzeit benutzerdefinierte Funktionen und Variablen bereitzustellen, indem Sie EvaluateFunction / EvaluateParameter-Ereignisse verarbeiten. Beispielausdrücke, die analysiert werden können:

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

Es behandelt auch Unicode und viele Datentypen nativ. Es wird mit einer Geweihdatei geliefert, wenn Sie die Grammatik ändern möchten. Es gibt auch eine Gabel, die MEF unterstützt, um neue Funktionen zu laden.

GreyCloud
quelle
1
Tolle Bibliothek. Auch verfügbar auf NUGET
Paul Grimshaw
Dies habe ich für meinen Differentialgleichungslöser verwendet, der die Eingabe eines Benutzers vorgenommen hat. Die Frage ist hier
kleineg
15

Eigentlich gibt es eine Art eingebauten - Sie können den XPath-Namespace verwenden! Obwohl es erforderlich ist, dass Sie die Zeichenfolge neu formatieren, um sie mit der XPath-Notation zu bestätigen. Ich habe eine Methode wie diese verwendet, um mit einfachen Ausdrücken umzugehen:

    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
quelle
12

Anfangs habe ich den c # -Wrapper für Muparser verwendet . Das war sehr schnell. Die einzige schnellere Lösung, die ich kenne, ist exprtk . Wenn Sie nach anderen Lösungen suchen, können Sie den Benchmark überprüfen .

Im Fall von .Net können Sie jedoch die integrierte Unterstützung verwenden, um Code zur Laufzeit zu kompilieren. Die Idee ist, eine "Vorlagen" -Quelldatei als eingebettete Ressource zu haben, in der Sie die Formel für die Auswertung ersetzen können. Dann übergeben Sie diesen vorbereiteten Klassenquellcode an den Compiler.

Eine grundlegende Vorlage könnte folgendermaßen aussehen:

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

Beachten Sie die% -Formel%, in die der Ausdruck eingefügt wird.

Verwenden Sie zum Kompilieren die Klasse CSharpCodeProvider. Ich möchte hier nicht die vollständige Quelle eintragen. Aber diese Antwort könnte helfen:

Nachdem Sie die In-Memory-Assembly geladen haben, können Sie eine Instanz Ihrer Klasse erstellen und EvalCode aufrufen.

schoetbi
quelle
9

Eine weitere Option, sobald Roslyn verfügbar ist:

Hierfür können Sie die CodeAnalysis.CSharp.Scripting-Bibliothek verwenden.

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

Nuget-Pakete:

<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
quelle
2
Dies sollte jetzt die Antwort sein
Sonofaforester
8

Kürzlich habe ich mXparser verwendet, eine Mathe-Parser-Bibliothek für .NET und JAVA. mXparser unterstützt sowohl grundlegende als auch sehr ausgefallene / komplizierte Formeln (einschließlich Variablen, Funktionen, Operatoren, Iteration und Rekursion).

https://mxparser.codeplex.com/

https://mathparser.org/

Einige Anwendungsbeispiele:

Beispiel 1:

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

Beispiel 2:

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

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

Kürzlich gefunden - falls Sie die Syntax ausprobieren möchten (und den erweiterten Anwendungsfall sehen möchten), können Sie den Skalar herunterladen Calculator- App , die von mXparser unterstützt wird.

Freundliche Grüße

Leroy Kegan
quelle
Dies ist die beste Bibliothek, die ich gefunden habe. Es wird jedoch kein anderer Datentyp als number unterstützt! Ich brauche DateTime und String. Kennen Sie eine gute Alternative?
Homam
5

Wenn Sie eine sehr einfache Sache brauchen, können Sie die DataTable:-) verwenden

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
quelle
3

Ein einfacher Mathe-Parser ist recht einfach zu erstellen und erfordert nur wenige Codezeilen:

Nehmen Sie dieses flexible Beispiel:

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

Um die Priorität durch Klammern zu aktivieren, reicht ein Stapel von Stapeln aus, z. B. durch Rekursion archiviert. Alles zwischen Klammern wird auf einen neuen Stapel gelegt. Schließlich können Sie mathematische Operationen von Lambdas auf eine sauber lesbare Weise unterstützen.

Lorenz Lo Sauer
quelle
Möglicherweise möchten Sie Ihre Antwort überprüfen. 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
quelle
3
-1: Füge dies einfach in die @ cbp-Antwort ein. Es gibt keine Notwendigkeit, zwei Antworten zu haben, die grundsätzlich identisch sind, wenn wir eine fantastische Antwort haben können.
Robert MacLean
1

Ich habe vor einigen Jahren einen Ausdrucksparser implementiert und kürzlich eine Version davon in GitHub und Nuget veröffentlicht: Albatross.Expression . Es enthält eine ExecutionContext-Klasse, die eine Reihe von Ausdrücken auswerten kann, z.

  • MV = Preis * Menge;
  • Preis = (Bid + Ask) / 2;
  • Gebot = 0,6;
  • Fragen Sie = .8;

Es hat auch eine Rundschreiben-Referenzprüfung eingebaut, die nützlich ist, um einen Stapelüberlauf zu vermeiden.

Rushui Guan
quelle
1

Sie können die Math-Expression-Evaluator- Bibliothek verwenden, deren Autor ich bin. Es unterstützt einfache Ausdrücke wie 2.5+5.9, 17.89-2.47+7.16, 5/2/2+1.5*3+4.58, Ausdrücke mit Klammern (((9-6/2)*2-4)/2-6-1)/(2+24/(2+4))und Ausdrücke mit Variablen:

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

Sie können Parameter auch als benannte Variablen übergeben:

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 unterstützt .Net Standard 2.0 und kann daher sowohl in .Net Core- als auch in .Net Full Framework-Projekten verwendet werden. Es gibt keine externen Abhängigkeiten.

Giorgi
quelle
0

Flee Fast Lightweight Expression Evaluator

https://flee.codeplex.com

Sprach-Referenz

  • ArithmeticOperators Beispiel: a * 2 + b ^ 2 - 100% 5
  • Beispiel für Vergleichsoperatoren: a <> 100
  • AndOrXorNotOperators Beispiel (logisch): a> 100 und nicht b = 100
  • ShiftOperators Beispiel: 100 >> 2
  • Verkettungsbeispiel: "abc" + "def"
  • Indizierungsbeispiel: arr [i + 1] + 100
  • Literale
  • Casting-Beispiel: 100 + Cast (obj, int)
  • ConditionalOperator Beispiel: Wenn (a> 100 und b> 10, "beide größer", "kleiner")
  • InOperator-Beispiel (Liste): If (100 in (100, 200, 300, -1), "in", "not in")
  • Überladene Operatoren für Typen

Beispiel:

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)

Die Ausgabe: 50

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

Vergiss nicht zu laden

<PackageReference Include="MathNet.Symbolics" Version="0.20.0" />
Künstliche Dummheit
quelle