Есть ли в .NET оценщик математики строки? - PullRequest
79 голосов
/ 10 декабря 2008

Если у меня есть строка с допустимым математическим выражением, например:

String s = "1 + 2 * 7";

Есть ли встроенная библиотека / функция в .NET, которая будет анализировать и оценивать это выражение для меня и возвращать результат? В этом случае 15.

Ответы [ 15 ]

57 голосов
/ 14 сентября 2013

Странно, что этот знаменитый и старый вопрос не имеет ответа, который предлагает встроенный DataTable.Compute - «трюк». Вот оно.

double result = Convert.ToDouble(new DataTable().Compute("1 + 2 * 7", null));

В выражениях поддерживаются следующие арифметические операторы:

+ (addition)
- (subtraction)
* (multiplication)
/ (division)
% (modulus)

Дополнительная информация: DataColumn.Expression при Синтаксис выражения .

52 голосов
/ 25 декабря 2008

Вы можете добавить ссылку на Microsoft Script Control Library (COM) и использовать подобный код для оценки выражения. (Также работает для JScript.)

Dim sc As New MSScriptControl.ScriptControl()
sc.Language = "VBScript"
Dim expression As String = "1 + 2 * 7"
Dim result As Double = sc.Eval(expression)

Редактировать - C # версия.

MSScriptControl.ScriptControl sc = new MSScriptControl.ScriptControl();
sc.Language = "VBScript";
string expression = "1 + 2 * 7";
object result = sc.Eval(expression);            
MessageBox.Show(result.ToString());

Редактировать - ScriptControl является COM-объектом. В диалоговом окне «Добавить ссылку» проекта выберите вкладку «COM», прокрутите вниз до «Microsoft Script Control 1.0» и выберите ok.

28 голосов
/ 25 декабря 2008

Для тех, кто разрабатывает в C # на Silverlight, вот довольно приятный трюк, который я только что обнаружил, который позволяет оценить выражение, вызывая движок Javascript:

double result = (double) HtmlPage.Window.Eval("15 + 35");
21 голосов
/ 09 декабря 2010

Вы видели http://ncalc.codeplex.com?

Это расширяемое, быстрое (например, имеет собственный кэш) позволяет вам предоставлять пользовательские функции и переменные во время выполнения путем обработки событий EvaluateFunction / EvaluateParameter. Примеры выражений, которые он может анализировать:

Expression e = new Expression("Round(Pow(Pi, 2) + Pow([Pi2], 2) + X, 2)"); 

  e.Parameters["Pi2"] = new Expression("Pi * Pi"); 
  e.Parameters["X"] = 10; 

  e.EvaluateParameter += delegate(string name, ParameterArgs args) 
    { 
      if (name == "Pi") 
      args.Result = 3.14; 
    }; 

  Debug.Assert(117.07 == e.Evaluate()); 

Он также обрабатывает Unicode и многие типы данных изначально. Он поставляется с файлом рога, если вы хотите изменить грамматику. Существует также форк, который поддерживает MEF для загрузки новых функций.

15 голосов
/ 04 февраля 2010

На самом деле есть своего рода встроенный - вы можете использовать пространство имен XPath! Хотя это требует, чтобы вы переформатировали строку, чтобы подтвердить с примечанием XPath. Я использовал такой метод для обработки простых выражений:

    public static double Evaluate(string expression)
    {
        var xsltExpression = 
            string.Format("number({0})", 
                new Regex(@"([\+\-\*])").Replace(expression, " ${1} ")
                                        .Replace("/", " div ")
                                        .Replace("%", " mod "));

        return (double)new XPathDocument
            (new StringReader("<r/>"))
                .CreateNavigator()
                .Evaluate(xsltExpression);
    }
11 голосов
/ 19 января 2016

Изначально я использовал оболочку c # для muparser . Это было очень быстро. Единственное быстрое решение, которое я знаю, это exprtk . Если вы ищете другие решения, вы можете проверить тест .

Но в случае .Net вы можете использовать встроенную поддержку для компиляции кода во время выполнения. Идея состоит в том, чтобы иметь исходный файл шаблона, например, встроенный ресурс, где можно заменить формулу для оценки. Затем вы передаете этот подготовленный исходный код класса компилятору.

Базовый шаблон может выглядеть так:

public class CSCodeEvaler
{
    public double EvalCode()
    {
        return last = Convert.ToDouble(%formula%);
    }

    public double last = 0;
    public const double pi = Math.PI;
    public const double e = Math.E;
    public double sin(double value) { return Math.Sin(value); }
    public double cos(double value) { return Math.Cos(value); }
    public double tan(double value) { return Math.Tan(value); }
    ...

Обратите внимание на% формулу%, в которую будет вставлено выражение.

Для компиляции используйте класс CSharpCodeProvider. Я не хочу помещать в полный источник здесь. Но этот ответ может помочь:

После загрузки сборки в памяти вы можете создать экземпляр вашего класса и вызвать EvalCode.

8 голосов
/ 04 марта 2016

Недавно я использовал mXparser, библиотеку математических парсеров для .NET и JAVA. mXparser поддерживает основные формулы, а также очень сложные / сложные (включая переменные, функции, операторы, итерацию и рекурсию).

https://mxparser.codeplex.com/

http://mathparser.org/

Несколько примеров использования:

Пример 1:

Expression e = new Expression("1+2*7 + (sin(10) - 2)/3");
double v = e.calculate();

Пример 2:

Argument x = new Argument("x = 5");
Expression e = new Expression("2*x+3", x);
double v = e.calculate();

Пример 3:

Function f = new Function("f(x,y) = sin(x) / cos(y)");
Expression e = new Expression("f(pi, 2*pi) - 2", f);
double v = e.calculate();

Найден недавно - если вы хотите попробовать синтаксис (и посмотреть расширенный вариант использования), вы можете загрузить Скалярный калькулятор приложение , работающее на mXparser. *

С уважением

7 голосов
/ 22 марта 2016

Еще один вариант теперь, когда Roslyn доступен:

Для этого вы можете использовать библиотеку CodeAnalysis.CSharp.Scripting.

using Microsoft.CodeAnalysis.CSharp.Scripting;
using System;

namespace ExpressionParser
{
    class Program
    {
        static void Main(string[] args)
        {
            //Demonstrate evaluating C# code
            var result = CSharpScript.EvaluateAsync("System.DateTime.Now.AddDays(-1) > System.DateTime.Now").Result;
            Console.WriteLine(result.ToString());

            //Demonstrate evaluating simple expressions
            var result2 = CSharpScript.EvaluateAsync(" 5 * 7").Result;
            Console.WriteLine(result2);
            Console.ReadKey();
        }
    }
}

пакетов nuget:

<package id="Microsoft.CodeAnalysis.Analyzers" version="1.1.0" targetFramework="net461" />
<package id="Microsoft.CodeAnalysis.Common" version="1.1.1" targetFramework="net461" />
<package id="Microsoft.CodeAnalysis.CSharp" version="1.1.1" targetFramework="net461" />
<package id="Microsoft.CodeAnalysis.CSharp.Scripting" version="1.1.1" targetFramework="net461" />
<package id="Microsoft.CodeAnalysis.Scripting" version="1.1.1" targetFramework="net461" />
<package id="Microsoft.CodeAnalysis.Scripting.Common" version="1.1.1" targetFramework="net461" />
5 голосов
/ 15 ноября 2011

Если вам нужна очень простая вещь, вы можете использовать DataTable: -)

Dim dt As New DataTable
dt.Columns.Add("A", GetType(Integer))
dt.Columns.Add("B", GetType(Integer))
dt.Columns.Add("C", GetType(Integer))
dt.Rows.Add(New Object() {12, 13, DBNull.Value})

Dim boolResult As Boolean = dt.Select("A>B-2").Length > 0

dt.Columns.Add("result", GetType(Integer), "A+B*2+ISNULL(C,0)")
Dim valResult As Object = dt.Rows(0)("result")
3 голосов
/ 21 июля 2016

Простой математический парсер довольно прост в сборке и требует всего несколько строк кода:

Возьмите этот гибкий пример:

class RPN
{
    public static double Parse( Stack<string> strStk )
    {
        if (strStk == null || strStk.Count == 0 )
        {
            return 0;
        }
        Stack<double> numStk = new Stack<double>();
        double result = 0;

        Func<double, double> op = null;
        while (strStk.Count > 0)
        {
            var s = strStk.Pop();
            switch (s)
            {
                case "+":
                    op = ( b ) => { return numStk.Pop() + b; };
                    break;
                case "-":
                    op = ( b ) => { return numStk.Pop() - b; };
                    break;
                case "*":
                    op = ( b ) => { return numStk.Pop() * b; };
                    break;
                case "/":
                    op = ( b ) => { return numStk.Pop() / b; };
                    break;

                default:
                    double.TryParse(s, NumberStyles.Any, out result);
                    if (numStk.Count > 0)
                    {
                        result = op(result);
                    }
                    numStk.Push(result);
                    break;
            }
        }
        return result;
    }
}

....
var str = " 100.5 + 300.5 - 100 * 10 / 100";    
str = Regex.Replace(str, @"\s", "", RegexOptions.Multiline);
Stack<string> strStk = new Stack<string>(
     Regex.Split(str, @"([()*+\/-])", RegexOptions.Multiline).Reverse()
);
RPN.Parse(strStk);

Чтобы включить приоритет при заключении в скобки стека, будет достаточно, например, заархивированного рекурсией. Все, что находится в скобках, помещается в новый стек. Наконец, вы можете поддерживать математические операции в удобном для чтения виде с помощью lambdas.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...