web-dev-qa-db-fra.com

Chaîne d'évaluation "3 * (4 + 2)" rendement int 18

Existe-t-il une fonction du framework .NET qui peut évaluer une expression numérique contenue dans une chaîne et renvoyer le résultat? F.e .:

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

Existe-t-il une fonction-cadre standard avec laquelle vous pouvez remplacer ma méthode EvaluateExpression?

94
sindre j

Oui, vous pouvez laisser le compilateur C # l’évaluer au moment de l’exécution.

Voir: CSharpCorner

41
arul

Si vous souhaitez évaluer une expression de chaîne, utilisez l'extrait de code ci-dessous.

using System.Data;

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

Utiliser le compilateur implique des fuites de mémoire car les assemblys générés sont chargés et jamais libérés. C'est aussi moins performant que d'utiliser un interprète d'expression réel. Pour ce faire, vous pouvez utiliser Ncalc qui est un framework open-source avec cette seule intention. Vous pouvez également définir vos propres variables et fonctions personnalisées si celles déjà incluses ne suffisent pas.

Exemple:

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

Essaye ça:

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

Explication de la façon dont cela fonctionne:

Premièrement, nous créons une table dans la partie var loDataTable = new DataTable();, comme dans un moteur de base de données (MS SQL par exemple).

Ensuite, une colonne, avec des paramètres spécifiques (var loDataColumn = new DataColumn("Eval", typeof (double), expression);).

Le paramètre "Eval" Est le nom de la colonne (attribut ColumnName).

typeof (double) est le type de données à stocker dans la colonne, ce qui revient à placer System.Type.GetType("System.Double"); à la place.

expression est la chaîne reçue par la méthode Evaluate et est stockée dans l'attribut Expression de la colonne. Cet attribut a un objectif très spécifique (évident), à savoir que chaque ligne placée dans la colonne sera remplie avec "Expression" et accepte pratiquement tout ce qui peut être placé dans une requête SQL. Reportez-vous à http://msdn.Microsoft.com/en-us/library/system.data.datacolumn.expression (v = vs.100) .aspx pour savoir ce qui peut être placé dans l'expression attribut, et comment il est évalué.

Ensuite, loDataTable.Columns.Add(loDataColumn); ajoute la colonne loDataColumn à la table loDataTable.

Ensuite, une ligne est ajoutée à la table avec une colonne personnalisée avec un attribut Expression, effectuée via loDataTable.Rows.Add(0);. Lorsque nous ajoutons cette ligne, la cellule de la colonne "Eval" de la table loDataTable se remplit automatiquement avec son attribut "Expression" et, si elle contient des opérateurs et des requêtes SQL, etc., elle est évaluée puis stockée. à la cellule, alors, voici la "magie", la chaîne avec des opérateurs est évaluée et stockée dans une cellule ...

Enfin, il suffit de renvoyer la valeur stockée dans la cellule de la colonne "Eval" de la ligne 0 (il s’agit d’un index qui commence à compter à partir de zéro) et en effectuant une conversion en double avec return (double) (loDataTable.Rows[0]["Eval"]);.

Et c'est tout… travail accompli!

Et voici un code à comprendre, qui fait la même chose ... Ce n’est pas une méthode, c’est aussi expliqué.

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

Commencez par créer la table avec DataTable MyTable = new DataTable();

Ensuite, une colonne avec DataColumn MyColumn = new DataColumn();

Ensuite, nous mettons un nom à la colonne. Ceci afin que nous puissions rechercher dans son contenu lorsqu'il est stocké dans la table. Fait via MyColumn.ColumnName = "MyColumn";

Ensuite, dans Expression, nous pouvons mettre ici une variable de type chaîne. Dans ce cas, il existe une chaîne prédéfinie "5 + 5/5", dont le résultat est 6.

Le type de données à stocker dans la colonne MyColumn.DataType = typeof(double);

Ajouter la colonne à la table ... MyTable.Columns.Add(MyColumn);

Crée une ligne à insérer dans la table, qui copie la structure de la table DataRow MyRow = MyTable.NewRow();

Ajouter la ligne à la table avec MyTable.Rows.Add(MyRow);

Et renvoyer la valeur de la cellule dans la ligne 0 de la colonne MyColumn de la table MyTable avec return (double)(MyTable.Rows[0]["MyColumn"]);

Leçon faite !!!

14
mishamosher

Vous pouvez regarder "XpathNavigator.Evaluate". Je l'ai utilisé pour traiter des expressions mathématiques pour mon GridView et cela fonctionne bien pour moi.

Voici le code que j'ai utilisé pour mon programme:

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

C'est un évaluateur d'expression simple utilisant des piles

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

Ceci est une exécution de droite à gauche, il est donc nécessaire d’utiliser une parathèse appropriée pour exécuter une expression.

    // 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

J'ai récemment eu besoin de faire cela pour un projet et j'ai fini par utiliser IronPython pour le faire. Vous pouvez déclarer une instance du moteur, puis passer toute expression python valide) et obtenir le résultat. Si vous ne faites que des expressions mathématiques simples, cela suffira. Mon code a fini par paraître semblable à:

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

Vous ne voudrez probablement pas créer le moteur pour chaque expression. Vous avez également besoin d'une référence à IronPython.dll

3
Beardo

MODIFIER: Réalisé, je devrais vraiment faire l'addition et la soustraction séparément aussi bien pour le rendre un peu plus conforme BODMAS.

Un grand merci à Rajesh Jinaga pour son approche basée sur Stack. Je l'ai trouvé vraiment utile pour mes besoins. Le code suivant est une légère modification de la méthode de Rajesh, qui traite d'abord les divisions, puis les multiplications, puis se termine par l'addition et la soustraction. Cela permettra également l'utilisation de booleans dans les expressions, où true est traité comme 1 et false 0. permettant l'utilisation de la logique booléenne dans les expressions.

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

Je sais qu’il ya probablement une manière plus propre de le faire, pensa id. Il suffit de partager le premier regard au cas où quelqu'un le trouverait utile.

3
Fat-Wednesday

Vous pouvez assez facilement exécuter cela par le biais de CSharpCodeProvider avec un fluff approprié (un type et une méthode, en gros). De même, vous pouvez utiliser VB etc. - ou JavaScript, comme l’a suggéré une autre réponse. Je ne connais rien d’autre qui soit intégré au cadre pour le moment.

Je pense que .NET 4.0, avec sa prise en charge des langages dynamiques, pourrait avoir de meilleures capacités à cet égard.

3
Jon Skeet

Merci beaucoup à Ramesh. J'ai utilisé une version de son code simple pour extraire une chaîne d'une base de données et l'utiliser pour effectuer des opérations booléennes dans mon code.

x est un nombre comme 1500 ou 2100 ou autre.

fonction serait une évaluation stockée comme x> 1400 et 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

Il n'y a pas. Vous devrez utiliser une bibliothèque externe ou écrire votre propre analyseur. Si vous avez le temps de le faire, je suggère d’écrire votre propre analyseur, car c’est un projet assez intéressant. Sinon, vous devrez utiliser quelque chose comme bcParser .

1
Tamas Czinege