#c#
Вопрос:
Мне нужно оценить математическое выражение, которое представлено мне в виде строки в C#. Пример noddy, но указывает на то, что строка является выражением.
Мне нужна оценка, чтобы затем заполнить int.
В C# нет Eval (), как и в других языках…
String myString = "3*4";
Редактировать:
Я на VS2008
Попробовал «Майкрософт».JScript. = Его устаревший метод (но все еще соответствует — предупреждение)
Однако Microsoft.Библиотека dll JScript, над которой я работаю, работает
элемент вызова общедоступного объекта(имя строки, BindingFlags invokeAttr, Связующее связующее, цель объекта, аргументы объекта []);
Жалуется, что чего-то не хватает «;» иди разберись…
ПРАВКА 2
Решение — было кодовым — оно сработало, так как нет проблем с безопасностью — только я когда-либо буду запускать код. Большое спасибо за ответы …
И ссылка на новую книгу о Драконах потрясающая
ПРАВКА 3
Мэтт доступен для данных.Функция Compute() также работает — даже лучше для тех, кто заботится о безопасности. (отмечена проверка параметров)
Комментарии:
1. Вы также можете посмотреть на Ncalc.codeplex.com как я и предполагал в своем ответе, который, похоже, был удален по причинам, которых я не понимаю.
Ответ №1:
Все остальные ответы-возможный перебор.
Если все, что вам нужно, — это простая арифметика, сделайте это.
DataTable dummy = new DataTable();
Console.WriteLine(dummy.Compute("15 / 3",string.Empty));
ПРАВКА: немного больше информации. Ознакомьтесь с документацией MSDN для Expression
свойства System.Data.DataColumn
класса. Материал «Синтаксис выражений» содержит список команд, которые вы можете использовать в дополнение к арифметическим операторам. (напр. IIF, ЛЕН и т.д.). Спасибо всем за то, что проголосовали за мой первый опубликованный ответ!
Комментарии:
1. славный маленький хак. Просто любопытно: как это будет вести себя при плохих вводах?
2. он выдает систему.Данные. Исключение EvaluateException или исключение System.Data.SyntaxError, указывающее вам, что не так.
3. о, умно! @OP: Должен дать этому флагу ответа
![]()
Ответ №2:
На мой взгляд, у вас есть два варианта — использовать средство оценки выражений или построить, скомпилировать и запустить код C# на лету.
Я бы выбрал библиотеку оценщика выражений, так как вам не нужно беспокоиться о каких-либо проблемах безопасности. То есть, возможно, вы не сможете использовать генерацию кода в средах со средним уровнем доверия, таких как большинство серверов общего хостинга.
Вот пример создания кода для вычисления выражений: http://www.vbforums.com/showthread.php?t=397264
Комментарии:
1. Привет — библиотека dotMath, похоже, не была перенесена из workspace.gotdotnet.com — когда у них будет переход на msdn…
2. Да
Кажется, доступно в CodePlex, хотя: codeplex.com/dotMath/Release/ProjectReleases.aspx?ReleaseId=875
3. Вы также можете посмотреть на Ncalc.codeplex.com как я и предполагал в своем ответе, который, похоже, был удален по причинам, которых я не понимаю.
Ответ №3:
Я сделал это в качестве личного упражнения на C# несколько недель назад.
Это довольно много кода, и местами он плохо прокомментирован. Но это сработало со многими тестовыми случаями.
Наслаждайтесь!
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace StackOverflow
{
class Start
{
public static void Main(string[] args)
{
Evaluator ev;
string variableValue, eq;
Console.Write("Enter equation: ");
eq = Console.ReadLine();
while (eq != "quit")
{
ev = new Evaluator(eq);
foreach (Variable v in ev.Variables)
{
Console.Write(v.Name " = ");
variableValue = Console.ReadLine();
ev.SetVariable(v.Name, Convert.ToDecimal(variableValue));
}
Console.WriteLine(ev.Evaluate());
Console.Write("Enter equation: ");
eq = Console.ReadLine();
}
}
}
class EvalNode
{
public virtual decimal Evaluate()
{
return decimal.Zero;
}
}
class ValueNode : EvalNode
{
decimal value;
public ValueNode(decimal v)
{
value = v;
}
public override decimal Evaluate()
{
return value;
}
public override string ToString()
{
return value.ToString();
}
}
class FunctionNode : EvalNode
{
EvalNode lhs = new ValueNode(decimal.Zero);
EvalNode rhs = new ValueNode(decimal.Zero);
string op = " ";
public string Op
{
get { return op; }
set
{
op = value;
}
}
internal EvalNode Rhs
{
get { return rhs; }
set
{
rhs = value;
}
}
internal EvalNode Lhs
{
get { return lhs; }
set
{
lhs = value;
}
}
public override decimal Evaluate()
{
decimal result = decimal.Zero;
switch (op)
{
case " ":
result = lhs.Evaluate() rhs.Evaluate();
break;
case "-":
result = lhs.Evaluate() - rhs.Evaluate();
break;
case "*":
result = lhs.Evaluate() * rhs.Evaluate();
break;
case "/":
result = lhs.Evaluate() / rhs.Evaluate();
break;
case "%":
result = lhs.Evaluate() % rhs.Evaluate();
break;
case "^":
double x = Convert.ToDouble(lhs.Evaluate());
double y = Convert.ToDouble(rhs.Evaluate());
result = Convert.ToDecimal(Math.Pow(x, y));
break;
case "!":
result = Factorial(lhs.Evaluate());
break;
}
return resu<
}
private decimal Factorial(decimal factor)
{
if (factor < 1)
return 1;
return factor * Factorial(factor - 1);
}
public override string ToString()
{
return "(" lhs.ToString() " " op " " rhs.ToString() ")";
}
}
public class Evaluator
{
string equation = "";
Dictionary<string, Variable> variables = new Dictionary<string, Variable>();
public string Equation
{
get { return equation; }
set { equation = value; }
}
public Variable[] Variables
{
get { return new List<Variable>(variables.Values).ToArray(); }
}
public void SetVariable(string name, decimal value)
{
if (variables.ContainsKey(name))
{
Variable x = variables[name];
x.Value = value;
variables[name] = x;
}
}
public Evaluator(string equation)
{
this.equation = equation;
SetVariables();
}
public decimal Evaluate()
{
return Evaluate(equation, new List<Variable>(variables.Values));
}
public decimal Evaluate(string text)
{
decimal result = decimal.Zero;
equation = text;
EvalNode parsed;
equation = equation.Replace(" ", "");
parsed = Parse(equation, "qx");
if (parsed != null)
result = parsed.Evaluate();
return resu<
}
public decimal Evaluate(string text, List<Variable> variables)
{
foreach (Variable v in variables)
{
text = text.Replace(v.Name, v.Value.ToString());
}
return Evaluate(text);
}
private static bool EquationHasVariables(string equation)
{
Regex letters = new Regex(@"[A-Za-z]");
return letters.IsMatch(equation);
}
private void SetVariables()
{
Regex letters = new Regex(@"([A-Za-z] )");
Variable v;
foreach (Match m in letters.Matches(equation, 0))
{
v = new Variable(m.Groups[1].Value, decimal.Zero);
if (!variables.ContainsKey(v.Name))
{
variables.Add(v.Name, v);
}
}
}
#region Parse V2
private Dictionary<string, string> parenthesesText = new Dictionary<string, string>();
/*
* 1. All the text in first-level parentheses is replaced with replaceText plus an index value.
* (All nested parentheses are parsed in recursive calls)
* 2. The simple function is parsed given the order of operations (reverse priority to
* keep the order of operations correct when evaluating).
* a. Addition ( ), subtraction (-) -> left to right
* b. Multiplication (*), division (/), modulo (%) -> left to right
* c. Exponents (^) -> right to left
* d. Factorials (!) -> left to right
* e. No op (number, replaced parentheses)
* 3. When an op is found, a two recursive calls are generated -- parsing the LHS and
* parsing the RHS.
* 4. An EvalNode representing the root node of the evaluations tree is returned.
*
* Ex. 3 5 (3 5) * 8
* *
* / /
* 3 5 8
* /
* 3 5 * 8 3 5
*
* /
* 3 *
* /
* 5 8
*/
/// <summary>
/// Parses the expression and returns the root node of a tree.
/// </summary>
/// <param name="eq">Equation to be parsed</param>
/// <param name="replaceText">Text base that replaces text in parentheses</param>
/// <returns></returns>
private EvalNode Parse(string eq, string replaceText)
{
int randomKeyIndex = 0;
eq = eq.Replace(" ", "");
if (eq.Length == 0)
{
return new ValueNode(decimal.Zero);
}
int leftParentIndex = -1;
int rightParentIndex = -1;
SetIndexes(eq, ref leftParentIndex, ref rightParentIndex);
//remove extraneous outer parentheses
while (leftParentIndex == 0 amp;amp; rightParentIndex == eq.Length - 1)
{
eq = eq.Substring(1, eq.Length - 2);
SetIndexes(eq, ref leftParentIndex, ref rightParentIndex);
}
//Pull out all expressions in parentheses
replaceText = GetNextReplaceText(replaceText, randomKeyIndex);
while (leftParentIndex != -1 amp;amp; rightParentIndex != -1)
{
//replace the string with a random set of characters, stored extracted text in dictionary keyed on the random set of chars
string p = eq.Substring(leftParentIndex, rightParentIndex - leftParentIndex 1);
eq = eq.Replace(p, replaceText);
parenthesesText.Add(replaceText, p);
leftParentIndex = 0;
rightParentIndex = 0;
replaceText = replaceText.Remove(replaceText.LastIndexOf(randomKeyIndex.ToString()));
randomKeyIndex ;
replaceText = GetNextReplaceText(replaceText, randomKeyIndex);
SetIndexes(eq, ref leftParentIndex, ref rightParentIndex);
}
/*
* Be sure to implement these operators in the function node class
*/
char[] ops_order0 = new char[2] { ' ', '-' };
char[] ops_order1 = new char[3] { '*', '/', '%' };
char[] ops_order2 = new char[1] { '^' };
char[] ops_order3 = new char[1] { '!' };
/*
* In order to evaluate nodes LTR, the right-most node must be the root node
* of the tree, which is why we find the last index of LTR ops. The reverse
* is the case for RTL ops.
*/
int order0Index = eq.LastIndexOfAny(ops_order0);
if (order0Index > -1)
{
return CreateFunctionNode(eq, order0Index, replaceText "0");
}
int order1Index = eq.LastIndexOfAny(ops_order1);
if (order1Index > -1)
{
return CreateFunctionNode(eq, order1Index, replaceText "0");
}
int order2Index = eq.IndexOfAny(ops_order2);
if (order2Index > -1)
{
return CreateFunctionNode(eq, order2Index, replaceText "0");
}
int order3Index = eq.LastIndexOfAny(ops_order3);
if (order3Index > -1)
{
return CreateFunctionNode(eq, order3Index, replaceText "0");
}
//no operators...
eq = eq.Replace("(", "");
eq = eq.Replace(")", "");
if (char.IsLetter(eq[0]))
{
return Parse(parenthesesText[eq], replaceText "0");
}
return new ValueNode(decimal.Parse(eq));
}
private string GetNextReplaceText(string replaceText, int randomKeyIndex)
{
while (parenthesesText.ContainsKey(replaceText))
{
replaceText = replaceText randomKeyIndex.ToString();
}
return replaceText;
}
private EvalNode CreateFunctionNode(string eq, int index, string randomKey)
{
FunctionNode func = new FunctionNode();
func.Op = eq[index].ToString();
func.Lhs = Parse(eq.Substring(0, index), randomKey);
func.Rhs = Parse(eq.Substring(index 1), randomKey);
return func;
}
#endregion
/// <summary>
/// Find the first set of parentheses
/// </summary>
/// <param name="eq"></param>
/// <param name="leftParentIndex"></param>
/// <param name="rightParentIndex"></param>
private static void SetIndexes(string eq, ref int leftParentIndex, ref int rightParentIndex)
{
leftParentIndex = eq.IndexOf('(');
rightParentIndex = eq.IndexOf(')');
int tempIndex = eq.IndexOf('(', leftParentIndex 1);
while (tempIndex != -1 amp;amp; tempIndex < rightParentIndex)
{
rightParentIndex = eq.IndexOf(')', rightParentIndex 1);
tempIndex = eq.IndexOf('(', tempIndex 1);
}
}
}
public struct Variable
{
public string Name;
public decimal Value;
public Variable(string n, decimal v)
{
Name = n;
Value = v;
}
}
}
Комментарии:
1. Почему бы не создать
EvalNode
интерфейс? Все еще 1.2. При первоначальной работе над оценкой я просто хотел, чтобы код что-то возвращал. Эволюция кода привела меня к тому, что было опубликовано, и вы правы, EvalNode можно изменить на интерфейс.
3. Не удается с помощью скобок. Например:
(1 3) 2
Ответ №4:
Вы можете использовать интерпретатор jscript. Отличная статья для этого есть здесь: http://www.odetocode.com/Articles/80.aspx
Ответ №5:
Когда вы говорите «как на других языках», вы должны вместо этого сказать «как на динамических языках».
Для динамических языков, таких как python, ruby и многих интерпретируемых языков, функция Eval() является естественным элементом. На самом деле, вероятно, даже довольно тривиально реализовать свой собственный.
Тем не менее, .Net по своей сути является статической, строго типизированной, скомпилированной платформой (по крайней мере, до тех пор, пока среда выполнения динамического языка не получит большей поддержки). Это имеет естественные преимущества, такие как безопасность внедрения кода и проверка типов во время компиляции, которые трудно игнорировать. Но это означает, что функция Eval() не очень хорошо подходит — она хочет иметь возможность скомпилировать выражение заранее. В такого рода платформах, как правило, существуют другие, более безопасные способы выполнения той же задачи.
Комментарии:
1. Схема гамбита-это динамическая, строго типизированная (необязательно) скомпилированная платформа, и у нее есть оценка.
2. Я полагаю, вы имели в виду «как в интерпретируемых языках».
3. Включил «интерпретированное» предложение в мой ответ — хотя и не совсем так, как просили.
Ответ №6:
Проверьте, Бегите
Ответ №7:
В MS есть образец, называемый Библиотекой динамических запросов. Команда LINQ предоставляет его для динамического построения запросов LINQ, таких как: Dim-запрос = Северный ветер.Продукты.Где(«CategoryID=2») Вы можете проверить, предлагает ли он элементарные математические возможности.
Ответ №8:
В интерпретируемом языке у вас может быть возможность оценить строку с помощью интерпретатора. В C# вам нужен синтаксический анализатор для языка, на котором написана строка (язык математических выражений). Это нетривиальное упражнение. Если вы хотите это сделать, используйте синтаксический анализатор рекурсивного спуска. В ранних главах «Книги о драконах» (Составители: Дизайн и т. Д. Ахо, Сетхи и Уллман — 1-е изд. 1977 или 2-е изд.2007) Есть хорошее объяснение того, что вам нужно сделать.
Альтернативой может быть включение в ваш проект компонента, написанного на perl, который должен быть доступен для .Теперь подключитесь к сети и используйте perl для оценки.
Ответ №9:
Потребуется ли вам доступ к значениям других переменных при вычислении выражения?
Ответ №10:
Интерпретатор jscript может это сделать, или вы можете написать свой собственный синтаксический анализатор, если выражение простое (будьте осторожны, оно очень быстро усложняется).
Я почти уверен, что в C# нет прямого метода «Eval(строка)», поскольку он не интерпретируется.
Имейте в виду, что интерпретация кода зависит от внедрения кода, будьте особенно осторожны
Ответ №11:
После некоторого поиска в Google я вижу, что есть возможность создавать и компилировать код на лету с помощью CodeDom. (См. учебное пособие).
Лично я не думаю, что такой подход является очень хорошей идеей, поскольку пользователь может вводить любой код, который он хочет, но это может быть область для изучения (например, только проверяя ввод и разрешая только числа и простые математические операции).
Ответ №12:
Некоторые другие предложения:
- Mono 2.0 (вышел сегодня) имеет метод оценки.
- Вы можете легко написать небольшой домен, специфичный для boo.
- Вы можете создать анализатор EBNF с рекурсивным спуском старой школы.
Ответ №13:
Я опубликовал источник для ультракомпактного (1 класс, Должно быть тривиально перенести это на C#. Есть и другие, которые могли бы сделать больше, но этот очень способный, и он крошечный.