it-swarm.com.de

Die Auswertung der Zeichenkette "3 * (4 + 2)" ergibt int 18

Gibt es eine .NET Framework-Funktion, die einen in einer Zeichenfolge enthaltenen numerischen Ausdruck auswerten und das Ergebnis zurückgeben kann? F .:

string mystring = "3*(2+4)";
int result = EvaluateExpression(mystring);
Console.Writeln(result); // Outputs 18

Gibt es eine Standard-Framework-Funktion, durch die Sie meine EvaluateExpression -Methode ersetzen können?

94
sindre j

Ja, Sie können den C # -Compiler zur Laufzeit auswerten lassen.

Siehe: CSharpCorner

41
arul

Wenn Sie einen Zeichenfolgenausdruck auswerten möchten, verwenden Sie den folgenden Codeausschnitt.

using System.Data;

DataTable dt = new DataTable();
var v = dt.Compute("3 * (2+4)","");
132
Ramesh Sambari

Die Verwendung des Compilers führt zu Speicherverlusten, da die generierten Assemblys geladen und nie freigegeben werden. Es ist auch weniger performant als die Verwendung eines echten Ausdrucksinterpreters. Zu diesem Zweck können Sie Ncalc verwenden. Dies ist ein Open-Source-Framework, das ausschließlich diese Absicht verfolgt. Sie können auch eigene Variablen und benutzerdefinierte Funktionen definieren, wenn die bereits enthaltenen nicht ausreichen.

Beispiel:

Expression e = new Expression("2 + 3 * 5");
Debug.Assert(17 == e.Evaluate());
73

Versuche dies:

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"]);
}
49
Petar Repac
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"]); 
} 

Erklärung, wie es funktioniert:

Zunächst erstellen wir eine Tabelle im Teil var loDataTable = new DataTable();, genau wie in einer Datenbank-Engine (z. B. MS SQL).

Dann eine Spalte mit einigen spezifischen Parametern (var loDataColumn = new DataColumn("Eval", typeof (double), expression);).

Der Parameter "Eval" Ist der Name der Spalte (ColumnName-Attribut).

typeof (double) ist der Datentyp, der in der Spalte gespeichert werden soll, und entspricht stattdessen put System.Type.GetType("System.Double");.

expression ist der String, den die Methode Evaluate empfängt und der im Attribut Expression der Spalte gespeichert ist. Dieses Attribut dient einem bestimmten Zweck (offensichtlich): Jede Zeile, die in die Spalte eingefügt wird, wird mit dem "Ausdruck" gefüllt und akzeptiert praktisch alles, was in eine SQL-Abfrage eingefügt werden kann. Lesen Sie http://msdn.Microsoft.com/en-us/library/system.data.datacolumn.expression (v = vs.100) .aspx , um zu erfahren, was in den Ausdruck eingefügt werden kann Attribut und wie es ausgewertet wird.

Dann fügt loDataTable.Columns.Add(loDataColumn); die Spalte loDataColumn der Tabelle loDataTable hinzu.

Anschließend wird der Tabelle eine Zeile mit einer personalisierten Spalte mit einem Ausdrucksattribut hinzugefügt, die über loDataTable.Rows.Add(0); erstellt wird. Wenn wir diese Zeile hinzufügen, wird die Zelle der Spalte "Eval" der Tabelle loDataTable automatisch mit dem Attribut "Expression" gefüllt. Wenn sie Operatoren und SQL-Abfragen usw. enthält, wird sie ausgewertet und dann gespeichert in die Zelle, also, hier passiert die "Magie", die Zeichenfolge mit Operatoren wird ausgewertet und in einer Zelle gespeichert ...

Zum Schluss geben Sie einfach den in der Zelle der Spalte "Eval" in Zeile 0 gespeicherten Wert zurück (es ist ein Index, der ab Null zählt) und konvertieren ihn mit return (double) (loDataTable.Rows[0]["Eval"]); in ein Double.

Und das ist alles ... Arbeit geleistet!

Und hier ein Code, der es verstehen möchte, der das Gleiche tut ... Es befindet sich nicht in einer Methode, und es wird auch erklärt.

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

Erstellen Sie zuerst die Tabelle mit DataTable MyTable = new DataTable();

Dann eine Spalte mit DataColumn MyColumn = new DataColumn();

Als nächstes geben wir der Spalte einen Namen. Auf diese Weise können wir den Inhalt durchsuchen, wenn er in der Tabelle gespeichert wird. Fertig mit MyColumn.ColumnName = "MyColumn";

Dann, der Ausdruck, hier können wir eine Variable vom Typ string setzen, in diesem Fall gibt es eine vordefinierte Zeichenfolge "5 + 5/5", deren Ergebnis 6 ist.

Der Datentyp, der in der Spalte MyColumn.DataType = typeof(double); gespeichert werden soll

Fügen Sie die Spalte der Tabelle hinzu ... MyTable.Columns.Add(MyColumn);

Erstellen Sie eine Zeile, die in die Tabelle eingefügt werden soll und die die Tabellenstruktur kopiert DataRow MyRow = MyTable.NewRow();

Fügen Sie die Zeile mit MyTable.Rows.Add(MyRow); zur Tabelle hinzu

Und geben Sie den Wert der Zelle in Zeile 0 der Spalte MyColumn der Tabelle MyTable mit return (double)(MyTable.Rows[0]["MyColumn"]); zurück

Lektion erledigt !!!

14
mishamosher

Sie können sich "XpathNavigator.Evaluate" ansehen. Ich habe dies verwendet, um mathematische Ausdrücke für mein GridView zu verarbeiten, und es funktioniert einwandfrei für mich.

Hier ist der Code, den ich für mein Programm verwendet habe:

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 ")));
}
14
Olav Botterli

Dies ist ein einfacher Ausdrucksauswerter, der Stacks verwendet

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

Dies ist die Ausführung von rechts nach links. Sie müssen also die richtige Parathese verwenden, um den Ausdruck auszuführen

    // 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());
    }
4
Rajesh Jinaga

Ich musste dies kürzlich für ein Projekt tun und habe IronPython verwendet, um es zu tun. Sie können eine Instanz der Engine deklarieren und dann einen beliebigen gültigen Ausdruck python= übergeben, um das Ergebnis zu erhalten. Wenn Sie nur einfache mathematische Ausdrücke ausführen, reicht dies aus. Mein Code sah schließlich aus ähnlich zu:

IronPython.Hosting.PythonEngine pythonEngine = new IronPython.Hosting.PythonEngine();
string expression = "3*(2+4)";
double result = pythonEngine.EvaluateAs<double>(expression);

Sie möchten wahrscheinlich nicht die Engine für jeden Ausdruck erstellen. Sie benötigen auch einen Verweis auf IronPython.dll

3
Beardo

BEARBEITEN: Es wurde mir klar, dass ich Addition und Subtraktion auch separat ausführen sollte, um die BODMAS-Kompatibilität zu verbessern.

Vielen Dank an Rajesh Jinaga für seinen Stack-basierten Ansatz. Ich fand es sehr nützlich für meine Bedürfnisse. Der folgende Code ist eine geringfügige Modifikation der Rajesh-Methode, bei der zuerst Divisionen, dann Multiplikationen und schließlich Additionen und Subtraktionen durchgeführt werden. Sie ermöglicht auch die Verwendung von Booleschen Werten in den Ausdrücken, wobei true als 1 und false 0 behandelt wird. Dies ermöglicht die Verwendung von Boolescher Logik in Ausdrücken.

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

Ich weiß, dass es wahrscheinlich eine sauberere Art gibt, es zu tun, dachte ich, ich teile nur den ersten Blick darauf, falls jemand es nützlich findet.

3
Fat-Wednesday

Sie können dies ziemlich einfach über den CSharpCodeProvider ausführen, indem Sie eine geeignete Flusenpackung verwenden (im Grunde genommen einen Typ und eine Methode). Ebenso könnten Sie VB etc - oder JavaScript durchgehen, wie eine andere Antwort vorgeschlagen hat. Ich kenne an dieser Stelle nichts anderes, was in das Framework eingebaut ist.

Ich würde erwarten, dass .NET 4.0 mit seiner Unterstützung für dynamische Sprachen in dieser Hinsicht möglicherweise bessere Funktionen bietet.

3
Jon Skeet

Vielen Dank an Ramesh. Ich habe eine Version seines einfachen Codes verwendet, um einen String aus einer Datenbank zu ziehen und ihn für boolesche Operationen in meinem Code zu verwenden.

x ist eine Zahl wie 1500 oder 2100 oder was auch immer.

funktion wäre eine gespeicherte Auswertung wie x> 1400 und 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  }
2
Mr.Black

Da ist nicht. Sie müssen eine externe Bibliothek verwenden oder einen eigenen Parser schreiben. Wenn Sie die Zeit dazu haben, schlage ich vor, Ihren eigenen Parser zu schreiben, da es ein ziemlich interessantes Projekt ist. Andernfalls müssen Sie etwas wie bcParser verwenden.

1
Tamas Czinege