Question

    

Cette question a déjà une réponse ici:

         

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 d'infrastructure standard avec laquelle vous pouvez remplacer ma méthode EvaluateExpression ?

Était-ce utile?

La solution

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

Voir: CSharpCorner

Autres conseils

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

Utiliser le compilateur implique des fuites de mémoire car les assemblys générés sont chargés et ne sont jamais publiés. C'est aussi moins performant que d'utiliser un interprète d'expression réel. À cette fin, vous pouvez utiliser Ncalc , qui est un cadre open-source ayant 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());

Essayez ceci:

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

Vous pouvez consulter " XpathNavigator.Evaluate " J'ai utilisé cela pour traiter des expressions mathématiques pour mon GridView et cela fonctionne très 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 ")));
}
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 son fonctionnement:

Tout d'abord, 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 certains paramètres spécifiques ( var loDataColumn = new DataColumn ("Eval", typeof (double), expression); ).

Le paramètre "Eval" correspond au nom de la colonne (attribut ColumnName).

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

expression est la chaîne que la méthode Evaluate reçoit et est stockée dans l'attribut Expression de la colonne. Cet attribut est destiné à un objectif très spécifique (évident), à savoir que chaque ligne placée dans la colonne sera remplie avec le "Expression" et acceptera pratiquement tout ce qui peut être placé dans une requête SQL. Consultez http: / /msdn.microsoft.com/en-us/library/system.data.datacolumn.expression(v=vs.100).aspx pour savoir ce qui peut être placé dans l'attribut Expression 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 expression "Expression". attribut, et, s'il a des opérateurs et des requêtes SQL, etc., il est évalué et ensuite stocké dans la cellule, donc voici la "magie", la chaîne avec des opérateurs est évaluée et stockée dans une cellule ...

Enfin, il vous suffit de renvoyer la valeur stockée dans la cellule de la colonne "Eval". dans la ligne 0 (c'est un index, commence à compter à partir de zéro) et convertit en double avec return (double) (loDataTable.Rows [0] ["Eval"]); .

Et c'est tout ... le travail est fait!

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

Créez d'abord 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, l'expression, on peut mettre ici une variable de type chaîne, dans ce cas il y a 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);

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

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

Ajoutez 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] [" MaColonne &];));

Leçon faite !!!

Il s’agit d’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();
    }
}

Ceci est une exécution de droite à gauche, vous devez donc utiliser la parathèse appropriée pour exécuter l'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());
    }

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 dans le framework à ce stade.

Je m'attendrais à ce que .NET 4.0, avec sa prise en charge des langages dynamiques, offre de meilleures capacités à cet égard.

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 transmettre toute expression python valide et obtenir le résultat. Si vous ne faites que des expressions mathématiques simples, cela suffirait. Mon code a fini par ressembler à:

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 devez également faire référence à IronPython.dll

EDIT: j'ai réalisé que je devrais vraiment faire l'addition et la soustraction séparément afin de le rendre un peu plus compatible 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 façon plus propre de le faire, pense-t-il, mais partagez-le pour la première fois au cas où quelqu'un le trouverait utile.

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

x est un nombre tel que 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  }

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

Réponse courte: Je ne le pense pas. C # .Net est compilé (en bytecode) et ne peut pas évaluer les chaînes au moment de l'exécution, à ma connaissance. JScript .Net peut cependant; mais je vous conseillerais quand même de coder vous-même un analyseur et un évaluateur basé sur une pile.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top