La stringa di valutazione "3 * (4 + 2)" produce int 18 [duplicato]
Domanda
Questa domanda ha già una risposta qui:
Esiste una funzione del framework .NET in grado di valutare un'espressione numerica contenuta in una stringa e restituire il risultato? F.e:.
string mystring = "3*(2+4)";
int result = EvaluateExpression(mystring);
Console.Writeln(result); // Outputs 18
Esiste una funzione quadro standard che puoi sostituire il mio metodo EvaluateExpression
con?
Soluzione
Sì, puoi consentire al compilatore C # di valutarlo in fase di esecuzione.
Vedi: CSharpCorner
Altri suggerimenti
Se si desidera valutare un'espressione 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 per fare implica perdite di memoria poiché gli assembly generati vengono caricati e mai rilasciati. È anche meno performante rispetto all'utilizzo di un interprete di espressioni reali. A tale scopo puoi utilizzare Ncalc che è un framework open source con questo intento esclusivo. Puoi anche definire le tue variabili e 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"]);
}
Puoi 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("<r/>")).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:
Innanzitutto, creiamo una tabella nella parte var loDataTable = new DataTable ();
, proprio come in un motore di base di dati (ad esempio MS SQL).
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 archiviare nella colonna, che equivale a mettere System.Type.GetType (" System.Double "); invece.
espressione
è la stringa che riceve il metodo Valuta
ed è memorizzata nell'attributo Expression
della colonna. Questo attributo ha uno scopo veramente specifico (ovvio), ovvero che ogni riga inserita nella colonna verrà riempita con l'espressione "Espressione", e accetta praticamente che sia possibile inserire 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, viene aggiunta una riga alla tabella con una colonna personalizzata con un attributo Expression, eseguita tramite loDataTable.Rows.Add (0);
. Quando aggiungiamo questa riga, la cella della colonna " Eval " della tabella loDataTable
viene riempito automaticamente con la sua " espressione " attributo e, se ha operatori e query SQL, ecc., viene valutato e quindi archiviato nella cella, quindi, qui accade la "magia", la stringa con operatori viene valutata e memorizzata in una cella ...
Infine, restituisci semplicemente il valore memorizzato nella cella della colonna " Eval " nella riga 0 (è un indice, inizia a contare da zero) e fa una conversione in doppio con return (double) (loDataTable.Rows [0] [" Eval "]);
.
E questo è tutto ... lavoro fatto!
Ed ecco un programmatore che capisce, 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 in modo che possiamo cercare nel suo contenuto quando è memorizzato nella tabella. Fatto tramite MyColumn.ColumnName = " MyColumn " ;;
Quindi, l'Espressione, qui possiamo mettere una variabile di tipo stringa, in questo caso c'è una stringa predefinita " 5 + 5/5 " ;, il risultato è 6.
Il tipo di dati da archiviare 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 restituisce il valore della cella nella riga 0 della colonna MyColumn
della tabella MyTable
con return (double) (MyTable.Rows [0] [" MyColumn "]);
Lezione terminata !!!
Questo è un semplice strumento di valutazione delle espressioni che utilizza Stack
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();
}
}
Questo è giusto per l'esecuzione a sinistra, quindi è necessario utilizzare la paratesi corretta per eseguire l'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<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());
}
Potresti facilmente eseguirlo attraverso CSharpCodeProvider con un fluff adatto che lo avvolge (un tipo e un metodo, in sostanza). Allo stesso modo potresti passare attraverso VB ecc. O JavaScript, come suggerito da un'altra risposta. Non conosco nient'altro incorporato nel framework a questo punto.
Mi aspetto che .NET 4.0 con il suo supporto per i linguaggi dinamici possa avere migliori capacità su questo fronte.
Di recente ho dovuto farlo per un progetto e ho finito per usare IronPython per farlo. È possibile dichiarare un'istanza del motore, quindi passare qualsiasi espressione Python valida e ottenere il risultato. Se stai solo facendo semplici espressioni matematiche, sarebbe sufficiente. Il mio codice è risultato simile a:
IronPython.Hosting.PythonEngine pythonEngine = new IronPython.Hosting.PythonEngine();
string expression = "3*(2+4)";
double result = pythonEngine.EvaluateAs<double>(expression);
Probabilmente non vorrai creare il motore per ogni espressione. È inoltre necessario un riferimento a IronPython.dll
EDIT: Realizzato, dovrei davvero fare in modo che anche l'aggiunta e la sottrazione vengano separate separatamente per renderlo un po 'più conforme a BODMAS.
Un grande ringraziamento a Rajesh Jinaga per il suo approccio basato su 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, quindi le moltiplicazioni, quindi termina con l'aggiunta e la sottrazione. Consentirà inoltre l'uso di booleani nelle espressioni, dove true viene 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<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());
}
So che è probabile che ci sia un modo più pulito di farlo, ho pensato di condividere la prima occhiata nel caso qualcuno lo trovasse utile.
Mille grazie a Ramesh. Ho usato una versione del suo semplice codice per estrarre una stringa da un database e usarla per eseguire operazioni booleane nel mio codice.
x è un numero come 1500 o 2100 o qualsiasi altra cosa.
La funzionesarebbe 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'è. Sarà necessario utilizzare una libreria esterna o scrivere il proprio parser. Se hai il tempo di farlo, ti suggerisco di scrivere il tuo parser in quanto è un progetto piuttosto interessante. Altrimenti dovrai usare qualcosa come bcParser .
Risposta breve: non la penso così. C # .Net è compilato (in bytecode) e non posso valutare le stringhe in fase di esecuzione, per quanto ne so. JScript .Net può, tuttavia; ma ti consiglio comunque di codificare tu stesso un parser e un valutatore basato su stack.