112

文字列に含まれる数値式を評価して結果を返すことができる.NETフレームワークの関数はありますか? 鉄:

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

EvaluateExpression私のメソッドを置き換えることができる標準フレームワーク関数はありますか?

4

13 に答える 13

182

文字列式を評価する場合は、以下のコード スニペットを使用します。

using System.Data;

DataTable dt = new DataTable();
var v = dt.Compute("3 * (2+4)","");
于 2012-06-14T09:03:13.200 に答える
84

コンパイラを使用して行うことは、生成されたアセンブリが読み込まれ、決して解放されないため、メモリ リークを意味します。また、実式インタープリターを使用するよりもパフォーマンスが低くなります。この目的のために、この目的のみを目的としたオープンソース フレームワークであるNcalcを使用できます。すでに含まれているものでは不十分な場合は、独自の変数とカスタム関数を定義することもできます。

例:

Expression e = new Expression("2 + 3 * 5");
Debug.Assert(17 == e.Evaluate());
于 2009-10-14T07:47:01.253 に答える
14
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");代わりにputと同じです。

expressionEvaluateメソッドが受け取る文字列であり、Expression列の属性に格納されます。この属性は、本当に特定の目的(明らか)です。つまり、列に配置されるすべての行が「式」で埋められ、SQLクエリに配置できる実質的にすべての行を受け入れます。Expression属性に何を入れることができ、どのように評価されるかについては、 http://msdn.microsoft.com/en-us/library/system.data.datacolumn.expression( v = vs.100 ).aspxを参照してください。

次に、列をテーブルにloDataTable.Columns.Add(loDataColumn);追加します。loDataColumnloDataTable

次に、を介して、Expression属性を持つパーソナライズされた列を持つ行がテーブルに追加されますloDataTable.Rows.Add(0);。この行を追加すると、テーブルの「Eval」列のセルがloDataTable「Expression」属性で自動的に埋められ、演算子やSQLクエリなどがある場合は、評価されてセルに格納されます。ここで「魔法」が発生し、演算子を含む文字列が評価されてセルに格納されます。

最後に、行0の「Eval」列のセルに格納されている値を返し(これはインデックスであり、ゼロからカウントを開始します)、。を使用してdoubleに変換し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);

MyColumnそして、テーブルの列のMyTable行0のセルの値を次のように返します。return (double)(MyTable.Rows[0]["MyColumn"]);

レッスン完了!!!

于 2012-03-16T02:56:13.423 に答える
14

これは Stacks を使用した単純な Expression Evaluator です。

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();
    }
}
于 2010-11-16T14:54:20.537 に答える
7

これは右から左への実行であるため、適切な括弧を使用して式を実行する必要があります

    // 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());
    }
于 2011-03-14T12:18:48.637 に答える
4

編集:私は実際に足し算と引き算を別々に出して、BODMASにもう少し準拠させる必要があることに気付きました。

スタックベースのアプローチを提供してくれた Rajesh Jinaga に感謝します。私のニーズには本当に便利だと思いました。次のコードは Rajesh の方法を少し変更したもので、最初に除算を処理し、次に乗算を処理し、最後に加算と減算を行います。また、式でブール値を使用できます。ここで、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());
    }

私はそれを行うよりクリーンな方法がある可能性が高いことを知っています.

于 2011-06-11T11:34:16.513 に答える
3

これは、CSharpCodeProvider を介して、適切なフラフ ラッピング (基本的に型とメソッド) を使用してかなり簡単に実行できます。同様に、別の回答が示唆しているように、VB などまたは JavaScript を使用することもできます。この時点で、フレームワークに組み込まれているものは他にありません。

動的言語をサポートする .NET 4.0 は、この面でより優れた機能を備えていると期待しています。

于 2008-12-02T12:00:48.570 に答える
3

私は最近、プロジェクトのためにこれを行う必要があり、IronPythonを使用してそれを行うことになりました。エンジンのインスタンスを宣言し、有効な Python 式を渡して結果を取得できます。単純な数式を実行するだけなら、それで十分です。私のコードは次のようになりました。

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

おそらく、式ごとにエンジンを作成したくないでしょう。IronPython.dll への参照も必要です。

于 2008-12-02T15:44:26.340 に答える
2

Rameshに感謝します。私は彼の単純なコードのバージョンを使用してデータベースから文字列を引き出し、それを使用してコードでブール演算を実行しました。

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  }
于 2012-09-21T22:55:41.610 に答える
1

存在しない。外部ライブラリを使用するか、独自のパーサーを作成する必要があります。時間があれば、独自のパーサーを作成することをお勧めします。これは非常に興味深いプロジェクトです。それ以外の場合は、 bcParserなどを使用する必要があります。

于 2008-12-02T12:03:45.787 に答える
-3

短い答え: そうは思いません。私の知る限り、C# .Net は (バイトコードに) コンパイルされており、実行時に文字列を評価できません。ただし、JScript .Net は可能です。ただし、パーサーとスタックベースのエバリュエーターを自分でコーディングすることをお勧めします。

于 2008-12-02T12:02:03.853 に答える