سؤال

هل توجد وظيفة في إطار عمل .NET يمكنها تقييم التعبير الرقمي الموجود في سلسلة وإرجاع النتيجة؟F.E.:

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

هل هناك وظيفة إطارية قياسية يمكنك استبدالها بـ my EvaluateExpression طريقة مع؟

هل كانت مفيدة؟

المحلول

نعم، يمكنك السماح لمترجم C# بتقييمه في وقت التشغيل.

يرى: CSharpCorner

نصائح أخرى

إذا كنت تريد تقييم تعبير سلسلة، استخدم مقتطف التعليمات البرمجية أدناه.

using System.Data;

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

إن استخدام المترجم للقيام بذلك يعني حدوث تسرب للذاكرة حيث يتم تحميل التجميعات التي تم إنشاؤها وعدم إصدارها مطلقًا.كما أنه أقل أداءً من استخدام مترجم التعبير الحقيقي.لهذا الغرض يمكنك استخدام نكالك وهو إطار مفتوح المصدر لهذا الغرض فقط.يمكنك أيضًا تحديد المتغيرات والوظائف المخصصة الخاصة بك إذا لم تكن تلك المضمنة بالفعل كافية.

مثال:

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

جرب هذا:

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

يمكنك إلقاء نظرة على "XpathNavigator.Evaluate" لقد استخدمت هذا لمعالجة التعبيرات الرياضية لـ GridView الخاص بي وهو يعمل بشكل جيد بالنسبة لي.

هذا هو الكود الذي استخدمته لبرنامجي:

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

شرح طريقة العمل :

أولا، نقوم بعمل جدول في الجزء var loDataTable = new DataTable();, ، تمامًا كما هو الحال في محرك قاعدة البيانات (MS SQL على سبيل المثال).

ثم عمود يحتوي على بعض المعلمات المحددة (var loDataColumn = new DataColumn("Eval", typeof (double), expression);).

ال "Eval" المعلمة هي اسم العمود (سمة ColumnName).

typeof (double) هو نوع البيانات التي سيتم تخزينها في العمود، وهو ما يعادل وضع System.Type.GetType("System.Double"); بدلاً من.

expression هي السلسلة التي Evaluate يتم استلام الطريقة وتخزينها في السمة Expression من العمود.هذه السمة مخصصة لغرض محدد حقًا (واضح)، وهو أن كل صف يتم وضعه في العمود سيتم ملؤه بـ "التعبير"، ويقبل عمليًا أي شيء يمكن وضعه في استعلام SQL.تشير إلى http://msdn.microsoft.com/en-us/library/system.data.datacolumn.expression(v=vs.100).aspx لمعرفة ما يمكن وضعه في سمة التعبير، وكيفية تقييمه.

ثم، loDataTable.Columns.Add(loDataColumn); يضيف العمود loDataColumn إلى loDataTable طاولة.

بعد ذلك، تتم إضافة صف إلى الجدول بعمود مخصص بسمة Expression، ويتم ذلك عبر loDataTable.Rows.Add(0);.عندما نضيف هذا الصف، خلية العمود "Eval" من الجدول loDataTable يتم ملؤها تلقائيًا باستخدام سمة "التعبير" الخاصة بها، وإذا كانت تحتوي على عوامل تشغيل واستعلامات SQL، وما إلى ذلك، فسيتم تقييمها ثم تخزينها في الخلية، لذلك، يحدث "السحر" هنا، حيث يتم تقييم السلسلة مع عوامل التشغيل وتخزينها في خلية...

أخيرًا، ما عليك سوى إرجاع القيمة المخزنة إلى خلية العمود "Eval" في الصف 0 (وهو فهرس، يبدأ العد من الصفر)، وإجراء التحويل إلى مزدوج باستخدام return (double) (loDataTable.Rows[0]["Eval"]);.

و هذا كل شيء...تم إنجاز المهمة!

وهنا كود أسهل للفهم، والذي يفعل نفس الشيء ...إنه ليس داخل الطريقة، وقد تم شرحه أيضًا.

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

أولاً، قم بإنشاء الجدول باستخدام DataTable MyTable = new DataTable();

ثم عمود مع DataColumn MyColumn = new DataColumn();

بعد ذلك، نضع اسمًا على العمود.هذا حتى نتمكن من البحث في محتوياته عند تخزينه على الجدول.تم عبر MyColumn.ColumnName = "MyColumn";

بعد ذلك، التعبير، هنا يمكننا وضع متغير من نوع السلسلة، في هذه الحالة هناك سلسلة محددة مسبقًا "5+5/5"، والنتيجة هي 6.

نوع البيانات التي سيتم تخزينها في العمود MyColumn.DataType = typeof(double);

أضف العمود إلى الجدول... MyTable.Columns.Add(MyColumn);

قم بإنشاء صف ليتم إدراجه في الجدول، والذي ينسخ بنية الجدول DataRow MyRow = MyTable.NewRow();

أضف الصف إلى الجدول باستخدام MyTable.Rows.Add(MyRow);

وإرجاع قيمة الخلية في الصف 0 من العمود MyColumn من الطاولة MyTable مع return (double)(MyTable.Rows[0]["MyColumn"]);

انتهى الدرس !!!

هذا مقيم تعبير بسيط يستخدم الأكوام

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

هذا هو التنفيذ من اليمين إلى اليسار، لذا يجب استخدام التظليل المناسب لتنفيذ التعبير

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

يمكنك تشغيل هذا بسهولة إلى حد ما من خلال CSharpCodeProvider مع تغليفه بالزغب المناسب (نوع وطريقة بشكل أساسي).وبالمثل، يمكنك المرور عبر VB وما إلى ذلك - أو JavaScript، كما اقترحت إجابة أخرى.لا أعرف أي شيء آخر مدمج في الإطار في هذه المرحلة.

أتوقع أن يتمتع .NET 4.0 بدعمه للغات الديناميكية بقدرات أفضل على هذه الجبهة.

لقد احتجت مؤخرًا إلى القيام بذلك لمشروع وانتهى بي الأمر باستخدامه أيرون بايثون للقيام بذلك.يمكنك الإعلان عن مثيل للمحرك، ثم تمرير أي تعبير بايثون صالح والحصول على النتيجة.إذا كنت تقوم فقط بتعابير رياضية بسيطة، فسيكون ذلك كافيًا.انتهى الكود الخاص بي ليبدو مشابهًا لما يلي:

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

ربما لا ترغب في إنشاء المحرك لكل تعبير.تحتاج أيضًا إلى مرجع إلى IronPython.dll

يحرر: أدركت أنه يجب عليّ حقًا إحضار عمليات الجمع والطرح بشكل منفصل أيضًا لجعلها أكثر توافقًا مع BODMAS.

شكرًا جزيلاً لراجيش جيناغا على منهجه القائم على Stack.لقد وجدت أنه مفيد حقا لاحتياجاتي.الكود التالي هو تعديل طفيف لطريقة راجيش، التي تعالج القسمة أولا، ثم الضرب، ثم تنتهي بالجمع والطرح.سيسمح أيضًا باستخدام القيم المنطقية في التعبيرات، حيث يتم التعامل مع true على أنه 1 وfalse 0.السماح باستخدام المنطق المنطقي في التعبيرات.

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

أعلم أنه من المحتمل أن تكون هناك طريقة أكثر نظافة للقيام بذلك، وأعتقد أنني سأشارك النظرة الأولى عليها في حال وجدها أي شخص مفيدة.

شكرا جزيلا لراميش.لقد استخدمت نسخة من الكود البسيط الخاص به لسحب سلسلة من قاعدة البيانات واستخدامها لإجراء عمليات منطقية في الكود الخاص بي.

x رقم مثل 1500 أو 2100 أو أي شيء آخر.

ستكون الوظيفة بمثابة تقييم مخزن مثل x > 1400 و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  }

لا يوجد.سوف تحتاج إلى استخدام بعض المكتبات الخارجية، أو كتابة المحلل اللغوي الخاص بك.إذا كان لديك الوقت للقيام بذلك، أقترح أن تكتب المحلل اللغوي الخاص بك لأنه مشروع مثير للاهتمام للغاية.وإلا سوف تحتاج إلى استخدام شيء من هذا القبيل bcParser.

اجابة قصيرة:أنا لا أعتقد ذلك.تم تجميع C# .Net (للتشفير الثانوي) ولا يمكنه تقييم السلاسل في وقت التشغيل، على حد علمي.ومع ذلك، يمكن لـ JScript .Net؛لكنني ما زلت أنصحك بترميز المحلل اللغوي والمقيم القائم على المكدس بنفسك.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top