Подтвердить что ты не робот

Операторы как строки

Мне нужно оценить математическое выражение, которое представлено мне как строка в С#. Пример noddy, но получает точку, через которую строка как выражение.

Мне нужна оценка, чтобы затем заполнить int.

В С# нет Eval(), как в других langugaes...

String myString = "3*4";

Edit:

Я на VS2008

Пробовал Microsoft.JScript. = Его устаревший метод (но по-прежнему соблюдает - предупреждение)

Однако dll Microsoft.JScript, с которым я работаю, работает на

общедоступный объект InvokeMember (строка имя, BindingFlags invokeAttr, Binder связующее, цель объекта, объект [] args);

Считает, что отсутствует ";" go figure...

ИЗМЕНИТЬ 2

Решение - был codeDom один - он работал, поскольку нет проблемы безопасности - только я когда-либо буду запускать код. Большое спасибо за ответы...

И ссылка на новую книгу Дракона удивительна

ИЗМЕНИТЬ 3

Matt dataTable.Compute() также работает - еще лучше для безопасности. (отмечена проверка параметров)

4b9b3361

Ответ 1

Как я вижу это, у вас есть два варианта - используйте оценщик выражений или конструкцию, скомпилируйте и запускать код С# на лету.

Я бы пошел с библиотекой оценки выражений, так как вам не нужно беспокоиться о каких-либо проблемах безопасности. То есть, возможно, вы не сможете использовать генерацию кода в средах среднего доверия, таких как большинство общедоступных серверов хостинга.

Вот пример для генерации кода для оценки выражений: http://www.vbforums.com/showthread.php?t=397264

Ответ 2

Все остальные ответы могут быть излишними.

Если вам нужна простая арифметика, сделайте это.

        DataTable dummy = new DataTable();
        Console.WriteLine(dummy.Compute("15 / 3",string.Empty));

EDIT: немного больше информации. Ознакомьтесь с документацией MSDN для свойства Expression класса System.Data.DataColumn. В "Синтаксисе выражений" описывается список команд, которые вы можете использовать в дополнение к арифметическим операторам. (например, IIF, LEN и т.д.). Спасибо всем, кто проголосовал за мой первый ответ!

Ответ 3

Я сделал это как личное упражнение на С# несколько недель назад.

Это довольно немного кода и плохо комментируется в местах. Но он работал с большим количеством тестовых примеров.

Наслаждайтесь!

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 result;
    }

    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 result;
    }

    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 && 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 && 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 && 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;
        }
    }
}

Ответ 5

Когда вы говорите "как на других языках", вы должны сказать "как в динамических языках".

Для динамических языков, таких как python, ruby ​​и многие интерпретируемые языки, функция Eval() является естественным элементом. На самом деле, возможно, даже довольно тривиально реализовать свои собственные.

Как бы то ни было .Net - это статическая, сильно типизированная, скомпилированная платформа (по крайней мере, до тех пор, пока динамическая среда Rational Runtime не получит больше поддержки). Это имеет естественные преимущества, такие как защита от кодового инъекций и проверка типа времени компиляции, которые трудно игнорировать. Но это означает, что функция Eval() не является такой хорошей подгонкой - она ​​хочет уметь заранее компилировать выражение. В такой платформе обычно существуют другие, более безопасные способы выполнения одной и той же задачи.

Ответ 6

Отъезд Flee

Ответ 7

MS имеет образец под названием Dynamic Query Library. Команда LINQ обеспечивает динамическое построение запросов LINQ, таких как: Dim query = Northwind.Products.Where( "CategoryID = 2" ) Вы можете проверить, предлагает ли он рудиментарные математические возможности.

Ответ 8

В интерпретируемом языке у вас может быть возможность оценить строку с помощью интерпретатора. В С# вам нужен парсер для языка, на котором написана строка (язык математических выражений). Это нетривиальное упражнение. Если вы хотите это сделать, используйте парсер рекурсивного спуска. Первые главы "Книги дракона" ( "Составители: дизайн и т.д." Ахо, Сети и Ульман - 1-е изд., 1977 или 2-е изд., 2007) имеют хорошее объяснение того, что вам нужно делать.

Альтернативой может быть включение в ваш проект компонента, написанного на Perl, который, как предполагается, теперь доступен для .NET, и использовать perl для оценки.

Ответ 9

jscript interpreter мог бы сделать, или вы можете написать свой собственный парсер, если выражение простое (будьте осторожны, он становится очень сложным),

Я уверен, что в С# нет прямого метода "Eval (string)", поскольку он не интерпретируется.

Имейте в виду, что если интерпретация кода подвержена инъекции кода, будьте осторожны:)

Ответ 10

Вам нужно получить доступ к значениям других переменных при вычислении выражения?

Ответ 11

После некоторого поиска в Google, я вижу там возможность создавать и компилировать код "на лету" с помощью CodeDom. (См. учебник).

Я лично не думаю, что этот подход является очень хорошей идеей, поскольку пользователь может ввести любой код, который ему нужен, но это может быть область для изучения (например, только проверка ввода и только разрешение чисел и простое математические операции).

Ответ 12

Некоторые другие предложения:

  • Моно 2.0 (вышел сегодня) имеет метод eval.
  • Вы можете легко написать небольшой домен, определенный в boo.
  • Вы можете создать старый парсер EBNF для школьного рекурсивного спуска.

Ответ 13

Я разместил источник для ультракомпактного (1 класс, 10 Кбайт) Java Math Evaluator на моем веб-сайте. Должно быть тривиально переносить это на С#. Есть и другие, которые могут сделать больше, но это очень способно, и оно крошечное.