文字列の評価“ 3 *(4 + 2)” yield int 18 [複製]
質問
この質問にはすでに回答があります:
文字列に含まれる数値式を評価し、結果を返すことができる.NETフレームワークの関数はありますか? F.e。:
string mystring = "3*(2+4)";
int result = EvaluateExpression(mystring);
Console.Writeln(result); // Outputs 18
EvaluateExpression
メソッドを置き換えることができる標準フレームワーク関数はありますか?
解決
はい、C#コンパイラに実行時に評価させることができます。
参照: CSharpCorner
他のヒント
文字列式を評価する場合は、以下のコードスニペットを使用します。
using System.Data;
DataTable dt = new DataTable();
var v = dt.Compute("3 * (2+4)","");
コンパイラを使用して実行すると、生成されたアセンブリがロードされ、リリースされないため、メモリリークが発生します。また、実際の式インタプリタを使用するよりもパフォーマンスが低下します。この目的のために、 Ncalc を使用できます。これは、この目的のみを持つオープンソースフレームワークです。既に含まれているものでは不十分な場合は、独自の変数とカスタム関数を定義することもできます。
例:
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"]);
}
仕組みの説明:
最初に、データベースエンジン(MS SQLなど)と同様に、 var loDataTable = new DataTable();
の部分にテーブルを作成します。
次に、特定のパラメーターを持つ列( var loDataColumn = new DataColumn(&quot; Eval&quot ;, typeof(double)、expression);
)。
&quot; Eval&quot;
パラメーターは、列の名前(ColumnName属性)です。
typeof(double)
は列に格納されるデータのタイプで、put System.Type.GetType(&quot; System.Double&quot;);
代わりに。
expression
は、 Evaluate
メソッドが受け取る文字列であり、列の属性 Expression
に格納されます。この属性は、本当に特定の目的(明らか)であり、列に配置されるすべての行が「式」でいっぱいになり、実際にwatheverがSQLクエリに配置されることを受け入れます。 http:/を参照してください。 /msdn.microsoft.com/en-us/library/system.data.datacolumn.expression(v=vs.100).aspx を使用して、Expression属性に含めることができるものと、その評価方法を確認します。
次に、 loDataTable.Columns.Add(loDataColumn);
は、 loDataTable
テーブルに列 loDataColumn
を追加します。
次に、 loDataTable.Rows.Add(0);
を使用して、Expression属性を持つパーソナライズされた列を含む行がテーブルに追加されます。この行を追加すると、列のセル「評価」テーブルの loDataTable
は、「式」で自動的にいっぱいになります。属性、および演算子やSQLクエリなどがある場合、評価されてセルに保存されるため、ここでは「マジック」が発生します。演算子を含む文字列が評価されてセルに保存されます...
最後に、列&quot; Eval&quot;のセルに保存された値を返すだけです。行0(インデックスであり、ゼロからカウントを開始)、および return(double)(loDataTable.Rows [0] [&quot; Eval&quot;]);
。
これですべて...仕事が完了しました!
そして、ここでは理解しやすいコードがありますが、これは同じことをしています...メソッド内ではなく、説明もされています。
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 =&quot; MyColumn&quot ;;
次に、式、ここでは文字列型の変数を配置できます。この場合、定義済みの文字列「5 + 5/5」があり、結果は6です
列に格納されるデータのタイプ MyColumn.DataType = typeof(double);
テーブルに列を追加します... MyTable.Columns.Add(MyColumn);
テーブルに行を挿入し、テーブル構造をコピーします DataRow MyRow = MyTable.NewRow();
MyTable.Rows.Add(MyRow);
そして、テーブル MyTable
の列 MyColumn
の行0のセルの値を return(double)(MyTable.Rows [0] [&quot; MyColumn&quot;]);
レッスン完了!!!
これは、スタックを使用した単純な式評価ツールです
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 を実行します。エンジンのインスタンスを宣言し、有効なpython式を渡して結果を取得できます。単純な数式を実行しているだけであれば、それで十分です。私のコードは次のようになりました:
IronPython.Hosting.PythonEngine pythonEngine = new IronPython.Hosting.PythonEngine();
string expression = "3*(2+4)";
double result = pythonEngine.EvaluateAs<double>(expression);
おそらく、各式のエンジンを作成したくないでしょう。 IronPython.dllへの参照も必要です
編集:気づいたことは、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());
}
私はそれを行うよりクリーンな方法がある可能性が高いことを知っています、誰かがそれを便利だと思う場合に備えて、idは単にそれを最初に見るだけだと思った。
ラメシュに感謝します。彼の単純なコードのバージョンを使用してデータベースから文字列を引き出し、それを使用してコード内でブール演算を実行しました。
xは1500や2100などの数字です。
関数は、x&gt;のような保存された評価です。 1400およびx&lt; 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は可能です。ただし、パーサーとスタックベースのエバリュエーターを自分でコーディングすることをお勧めします。