计算字符串“3 * (4 + 2)”的结果为 int18

是否有一个函数。NET 框架,它可以计算包含在字符串中的数值表达式并返回结果?女:

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

有没有一个标准的框架函数,你可以替换我的 EvaluateExpression方法?

209306 次浏览

您可以相当容易地通过 CSharpCodeProvider 运行它,并使用适当的 fluff 来包装它(基本上是一个类型和一个方法)。同样,你也可以浏览 VB 等-或 JavaScript,正如另一个答案所建议的那样。目前我不知道框架中还有什么内置的东西。

我希望. NET 4.0能够支持动态语言,在这方面有更好的能力。

简短的回答: 我不这么认为。C # .据我所知,Net 是编译的(字节码) ,不能在运行时计算字符串。JScript.但是,Net 可以; 但是我仍然建议您自己编写一个解析器和基于堆栈的求值程序。

没有。您将需要使用一些外部库,或者编写自己的解析器。如果您有时间这样做,我建议您编写自己的解析器,因为它是一个非常有趣的项目。否则,您将需要使用类似于 BcParser的东西。

我最近需要为一个项目这样做,我最终使用 铁蟒来完成它。您可以声明引擎的一个实例,然后传递任何有效的 python 表达式并获得结果。如果你只是在做简单的数学表达式,那就足够了。我的代码最终看起来类似于:

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

您可能不想为每个表达式创建引擎,还需要一个对 IronPython.dll 的引用

你可以看看“ XpathNavigator”。“评估”我已经使用它来处理我的 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 ")));
}

Try this:

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

使用编译器执行此操作意味着在加载生成的程序集并且永远不释放该程序集时存在内存泄漏。它的性能也不如使用真实表达式解释器。出于这个目的,你可以使用 Ncalc,它是一个开源框架,只有这个目的。如果已经包含的变量和自定义函数还不够,还可以定义自己的变量和自定义函数。

例如:

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

这是一个使用堆栈的简单表达式计算器

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

编辑: 意识到我应该把加法和减法分开来使它更符合 BODMAS。

非常感谢 Rajesh Jinaga 的基于 Stack 的方法。我发现它对我的需求非常有用。下面的代码是对 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());
}

I know there is likely to be a cleaner way of doing it, thought id just share the first look at it in case anyone finds it usefull.

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();部分中创建一个表,就像在数据库引擎(例如 MSSQL)中一样。

Then, a column, with some specific parameters (var loDataColumn = new DataColumn("Eval", typeof (double), expression);).

"Eval"参数是列的名称(ColumnName 属性)。

typeof (double) is the type of data to be stored in the column, which is equal to put System.Type.GetType("System.Double"); instead.

expressionEvaluate方法接收的字符串,存储在列的属性 Expression中。这个属性有一个非常明确的用途(显而易见) ,就是放在列上的每一行都将用“ Expression”填充,并且它实际上接受任何可以放在 SQL 查询中的内容。参考 http://msdn.microsoft.com/en-us/library/system.data.datacolumn.expression(v=vs.100).aspx,了解可以在 Expression 属性中放入什么,以及如何计算它。

然后,loDataTable.Columns.Add(loDataColumn);将列 loDataColumn添加到 loDataTable表中。

然后,通过 loDataTable.Rows.Add(0);向表中添加一行带有 Expression 属性的个性化列。当我们添加这一行时,表 loDataTable的列“ Eval”的单元格会自动填充“ Expression”属性,如果它有操作符和 SQL 查询等,它会被计算并存储到单元格中,所以,这里发生了“魔术”,带操作符的字符串被计算并存储到一个单元格中..。

最后,只需将存储的值返回到行0中“ Eval”列的单元格(它是一个索引,从零开始计数) ,然后使用 return (double) (loDataTable.Rows[0]["Eval"]);转换为 double。

就这样,任务完成了!

这里的代码更容易理解,也是一样的... ... 它不在方法中,它也被解释了。

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";完成

然后,表达式,在这里我们可以放一个类型为 string 的变量,在这个例子中,有一个预定义的 string“5 + 5/5”,结果是6。

The type of data to be stored to the column MyColumn.DataType = typeof(double);

将该列添加到表中... ... MyTable.Columns.Add(MyColumn);

创建一个要插入到表中的行,该行将复制表结构 DataRow MyRow = MyTable.NewRow();

MyTable.Rows.Add(MyRow);将该行添加到表中

并返回表 MyTableMyColumn列的第0行中的单元格的值和 return (double)(MyTable.Rows[0]["MyColumn"]);

教训结束了! ! !

如果要计算字符串表达式,请使用下面的代码段。

using System.Data;


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

Many thanks to Ramesh. I used a version of his simple code to pull a string out a database and use it to do boolean operations in my code.

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  }