Оценка строки «3*(4+2)» дает int 18 [дубликат]
Вопрос
На этот вопрос уже есть ответ здесь:
Есть ли в платформе .NET функция, которая может оценивать числовое выражение, содержащееся в строке, и возвращать результат?Ф.э.:
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)","");
Использование компилятора do подразумевает утечки памяти, поскольку сгенерированные сборки загружаются и никогда не выпускаются.Это также менее производительно, чем использование реального интерпретатора выражений.Для этой цели вы можете использовать Нкальк это фреймворк с открытым исходным кодом, предназначенный исключительно для этой цели.Вы также можете определить свои собственные переменные и пользовательские функции, если уже включенных недостаточно.
Пример:
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)
— тип данных, которые будут храниться в столбце, который равен put System.Type.GetType("System.Double");
вместо.
expression
это строка, которую Evaluate
метод получает и сохраняется в атрибуте Expression
колонны.Этот атрибут предназначен для действительно конкретной цели (очевидно), а именно: каждая строка, помещенная в столбец, будет заполнена «Выражением», и он принимает практически все, что можно поместить в SQL-запрос.Ссылаться на http://msdn.microsoft.com/en-us/library/system.data.datacolumn.expression(v=vs.100).aspx чтобы знать, что можно поместить в атрибут Expression и как он оценивается.
Затем, loDataTable.Columns.Add(loDataColumn);
добавляет столбец loDataColumn
к loDataTable
стол.
Затем в таблицу добавляется строка с персонализированным столбцом с атрибутом Expression, выполняемая с помощью loDataTable.Rows.Add(0);
.Когда мы добавляем эту строку, ячейка столбца «Оценка» таблицы 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";
Затем выражение, сюда мы можем поместить переменную типа string, в данном случае это предопределенная строка «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 с поддержкой динамических языков вполне может иметь лучшие возможности в этом плане.
Недавно мне нужно было сделать это для проекта, и в итоге я использовал ЖелезоПитон сделать это.Вы можете объявить экземпляр движка, а затем передать любое допустимое выражение Python и получить результат.Если вы просто выполняете простые математические выражения, этого будет достаточно.Мой код в итоге выглядел примерно так:
IronPython.Hosting.PythonEngine pythonEngine = new IronPython.Hosting.PythonEngine();
string expression = "3*(2+4)";
double result = pythonEngine.EvaluateAs<double>(expression);
Вероятно, вам не захочется создавать механизм для каждого выражения.Вам также понадобится ссылка на IronPython.dll.
РЕДАКТИРОВАТЬ: Я понял, что мне действительно следует разделить сложение и вычитание, чтобы сделать его немного более совместимым с BODMAS.
Большое спасибо Раджешу Джинаге за его подход, основанный на стеке.Я нашел это действительно полезным для моих нужд.Следующий код представляет собой небольшую модификацию метода Раджеша, который сначала обрабатывает деление, затем умножение, а затем завершает сложение и вычитание.Это также позволит использовать логические значения в выражениях, где 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 может;но я бы все равно посоветовал вам самостоятельно написать парсер и оценщик на основе стека.