Valutazione della stringa “3 * (4 + 2)” resa int 18

Esiste una funzione .NET framework in grado di valutare un’espressione numerica contenuta in una stringa e restituire il risultato? Fe:

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

Esiste una funzione di framework standard che puoi sostituire con il mio metodo EvaluateExpression ?

    Sì, puoi lasciare che il compilatore C # lo valuti in fase di runtime.

    Vedi: CSharpCorner

    Se si desidera valutare un’espressione di stringa, utilizzare lo snippet di codice seguente.

     using System.Data; DataTable dt = new DataTable(); var v = dt.Compute("3 * (2+4)",""); 

    L’uso del compilatore implica perdite di memoria man mano che gli assembly generati vengono caricati e mai rilasciati. È anche meno performante rispetto all’uso di un interprete di espressioni reali. A tale scopo è ansible utilizzare Ncalc, che è un framework open source con questo solo scopo. Puoi anche definire le tue variabili e le funzioni personalizzate se quelle già incluse non sono sufficienti.

    Esempio:

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

    Prova questo:

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

    Si potrebbe guardare “XpathNavigator.Evaluate”. Ho usato questo per elaborare espressioni matematiche per il mio GridView e funziona bene per me.

    Ecco il codice che ho usato per il mio programma:

     public static double Evaluate(string expression) { return (double)new System.Xml.XPath.XPathDocument (new StringReader("")).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"]); } 

    Spiegazione di come funziona:

    Per prima cosa, creiamo una tabella nella parte var loDataTable = new DataTable(); , proprio come in un Data Base Engine (MS SQL per esempio).

    Quindi, una colonna, con alcuni parametri specifici ( var loDataColumn = new DataColumn("Eval", typeof (double), expression); ).

    Il parametro "Eval" è il nome della colonna (attributo ColumnName).

    typeof (double) è il tipo di dati da memorizzare nella colonna, che è uguale a mettere System.Type.GetType("System.Double"); anziché.

    expression è la stringa che riceve il metodo Evaluate e viene memorizzata nell’attributo Expression della colonna. Questo attributo è per uno scopo veramente specifico (ovvio), ovvero che ogni riga che viene inserita nella colonna verrà riempita con “Espressione” e accetta praticamente wathever può essere inserita in una query SQL. Fare riferimento a http://msdn.microsoft.com/en-us/library/system.data.datacolumn.expression(v=vs.100).aspx per sapere cosa può essere inserito nell’attributo Expression e come viene valutato.

    Quindi, loDataTable.Columns.Add(loDataColumn); aggiunge la colonna loDataColumn alla tabella loDataTable .

    Quindi, una riga viene aggiunta alla tabella con una colonna personalizzata con un attributo Expression, eseguito tramite loDataTable.Rows.Add(0); . Quando aggiungiamo questa riga, la cella della colonna “Eval” della tabella loDataTable viene loDataTable automaticamente con il suo attributo “Expression” e, se ha operatori e query SQL, ecc., Viene valutata e quindi archiviata nella cella, quindi , qui accade la “magia”, la stringa con operatori viene valutata e memorizzata in una cella …

    Infine, è sufficiente restituire il valore memorizzato nella cella della colonna “Eval” nella riga 0 (è un indice, inizia il conteggio da zero) e effettuare una conversione in un double con return (double) (loDataTable.Rows[0]["Eval"]); .

    E questo è tutto … lavoro fatto!

    E qui un codice per capire, che fa lo stesso … Non è all’interno di un metodo, ed è anche spiegato.

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

    Innanzitutto, crea la tabella con DataTable MyTable = new DataTable();

    Quindi, una colonna con DataColumn MyColumn = new DataColumn();

    Successivamente, inseriamo un nome nella colonna. Questo così possiamo cercare nei contenuti quando sono archiviati sul tavolo. Fatto tramite MyColumn.ColumnName = "MyColumn";

    Quindi, l’espressione, qui possiamo inserire una variabile di tipo stringa, in questo caso c’è una stringa predefinita “5 + 5/5”, il cui risultato è 6.

    Il tipo di dati da memorizzare nella colonna MyColumn.DataType = typeof(double);

    Aggiungi la colonna alla tabella … MyTable.Columns.Add(MyColumn);

    Crea una riga da inserire nella tabella, che copia la struttura della tabella DataRow MyRow = MyTable.NewRow();

    Aggiungi la riga alla tabella con MyTable.Rows.Add(MyRow);

    E restituire il valore della cella nella riga 0 della colonna MyColumn della tabella MyTable con return (double)(MyTable.Rows[0]["MyColumn"]);

    Lezione fatta !!!

    Questo è un semplice Expression Evaluator che usa Stacks

     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 ops = new Stack(); Stack vals = new Stack(); 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(); } } 

    Questa è un’esecuzione da destra a sinistra, quindi è necessario usare la paratesi corretta per eseguire un’espressione

      // 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 stack = new Stack(); 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()); } 

    Si potrebbe facilmente eseguire questo attraverso CSharpCodeProvider con fluff adatto che lo avvolge (un tipo e un metodo, in pratica). Allo stesso modo potresti passare attraverso VB ecc. – o JavaScript, come suggerito da un’altra risposta. Non so di nient’altro incorporato nel framework a questo punto.

    Mi aspetto che .NET 4.0 con il suo supporto per le lingue dinamiche possa avere migliori funzionalità su questo fronte.

    Di recente avevo bisogno di farlo per un progetto e ho finito per usare IronPython per farlo. È ansible dichiarare un’istanza del motore e quindi passare qualsiasi espressione python valida e ottenere il risultato. Se stai semplicemente facendo semplici espressioni matematiche, allora sarebbe sufficiente. Il mio codice ha finito per sembrare simile a:

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

    Probabilmente non vorresti creare il motore per ogni espressione. Hai anche bisogno di un riferimento a IronPython.dll

    EDIT: Realizzato dovrei davvero portare l’aggiunta e la sottrazione anche separatamente per renderlo un po ‘più conforms a BODMAS.

    Un grande ringraziamento a Rajesh Jinaga per il suo approccio basato sullo Stack. L’ho trovato davvero utile per le mie esigenze. Il codice seguente è una leggera modifica del metodo di Rajesh, che elabora prima le divisioni, poi le moltiplicazioni, quindi termina con addizione e sottrazione. Permetterà anche l’uso di booleani nelle espressioni, dove true è trattato come 1 e falso 0. consentendo l’uso della logica booleana nelle espressioni.

     public static double Evaluate(string expr) { expr = expr.ToLower(); expr = expr.Replace(" ", ""); expr = expr.Replace("true", "1"); expr = expr.Replace("false", "0"); Stack stack = new Stack(); 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 list = stack.ToList(); 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()); } 

    So che è probabile che ci sia un modo più pulito di farlo, pensavo che l’id basta condividerne la prima occhiata nel caso qualcuno la trovasse utile.

    Mille grazie a Ramesh. Ho usato una versione del suo semplice codice per estrarre una stringa da un database e usarla per fare operazioni booleane nel mio codice.

    x è un numero come 1500 o 2100 o qualsiasi altra cosa.

    la funzione sarebbe una valutazione memorizzata come x> 1400 e 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 } 

    Non c’è. Dovrai usare qualche libreria esterna, o scrivere il tuo parser. Se hai tempo per farlo, ti suggerisco di scrivere il tuo parser in quanto è un progetto abbastanza interessante. Altrimenti dovrai usare qualcosa come bcParser .

    Risposta breve: non la penso così C # .Net è compilato (in bytecode) e non può valutare le stringhe in fase di esecuzione, per quanto ne so. JScript .Net può, tuttavia; ma ti consiglio comunque di codificare un parser e un valutatore stack-based.