Есть ли в .NET оценщик строковой математики?


95

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

String s = "1 + 2 * 7";

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


2
Не встроенный. Но есть довольно всесторонний один здесь .
— Стрелок


1
Вы можете использовать оценщик выражений (функция Eval в 100% управляемой .NET)
— jadsc

Я только что создал решение для вычисления математических выражений на C # только для кода. Вы можете увидеть код на сайте blackbeltcoder.com/Articles/algorithms/ac-expression-evaluator .
— Джонатан Вуд,

Кажется, в этой библиотеке есть ошибки.
— Philippe Lavoie

Ответы:


53

Вы можете добавить ссылку на 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» и выберите ОК.


2
Хотя это помечено как ответ, это было 10 лет назад, и сейчас COM вроде как мертв. Я предпочитаю ответ DataTable.Compute ниже.
— dwilliss

64

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

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

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

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

Дополнительная информация: DataColumn.Expressionв Expression Syntax .


был ответ от ma81xx 15 ноя 2011
— tatigo

1
Вы правы, но для этого не использовался метод Compute.
— Тим Шмелтер

В вашем примере показано, что System.InvalidCastException не обрабатывается кодом пользователя HResult = -2147467262 Как я могу это исправить?
— Юлия Ашомок

У меня это работает, вы использовали этот пример кода? Используйте отладчик и проверьте значение результата, тип указан.
— Тим Шмелтер

28

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

double result = (double) HtmlPage.Window.Eval("15 + 35");

Интересно, могли бы вы сослаться на это в другом месте. Наверное, нет, но было бы круто.
— Джоэл Кохорн,

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

Попробуйте вводить числа с нуля в начале, результат ненадежный. Например, «054 + 6» дает 50.
— Terry

9
@djerry, потому что числа с начальным нулем считаются восьмеричным оценщиком JS, а восьмеричное 054 равно десятичному 44.
— Андре Лерия

24

Вы видели 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 для загрузки новых функций.


1
Отличная библиотека. Также доступно на NUGET
— Пол Гримшоу

Это то, что я использовал для своего решателя дифференциальных уравнений, который принимал участие пользователя. Вопрос здесь
— kleineg 03

15

На самом деле есть своего рода встроенный - вы можете использовать пространство имен 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);
    }

12

Изначально я использовал оболочку 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); }
    ...

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

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

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


9

Еще одна опция теперь, когда доступен 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" />

2
Это должен быть ответ сейчас
— sonofaforester

8

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

https://mxparser.codeplex.com/

https://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.

С уважением


Это лучшая библиотека, которую я нашел. Однако он не поддерживает никаких типов данных, кроме числа! Мне нужны DateTime и String ... знаете ли вы хорошую альтернативу?
— Homam

@Homam Вы нашли альтернативу строковым операциям
— Рамакришна Редди,

5

Если вам нужна очень простая вещь, вы можете использовать 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

Я бы также посмотрел на Джейса ( https://github.com/pieterderycke/Jace ). Jace - это высокопроизводительный математический анализатор и механизм вычислений, который поддерживает все разновидности .NET (.NET 4.x, Windows Phone, Windows Store, ...). Джейс также доступен через NuGet: https://www.nuget.org/packages/Jace


3

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

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

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

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


Возможно, вы захотите проверить свой ответ. 100.5 + 300.5 - 100 * 10 / 100 = 30.1vs391
— Тавани

1
namespace CalcExp
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            double res = Evaluate("4+5/2-1");

            Console.WriteLine(res);

        }

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

// ReSharper disable PossibleNullReferenceException
            return (double)new XPathDocument
                (new StringReader("<r/>"))
                    .CreateNavigator()
                    .Evaluate(xsltExpression);
// ReSharper restore PossibleNullReferenceException
        }

    }
}

3
-1: просто объедините это с ответом @cbp. Нет нужды иметь два фундаментально идентичных ответа, когда у нас может быть один фантастический ответ.
— Роберт Маклин

1

Несколько лет назад я реализовал синтаксический анализатор выражений и недавно опубликовал его версию в GitHub и Nuget: Albatross.Expression . Он содержит класс ExecutionContext, который может оценивать набор выражений, таких как:

  • MV = Цена * Кол-во;
  • Цена = (Бид + Аск) / 2;
  • Ставка = .6;
  • Спросить = .8;

Он также имеет встроенную проверку циклических ссылок, которая помогает избежать переполнения стека.


1

Вы можете использовать библиотеку Math-Expression-Evaluator , автором которой я являюсь. Он поддерживает простые выражения , такие как 2.5+5.9, 17.89-2.47+7.16, 5/2/2+1.5*3+4.58, выражения со скобками (((9-6/2)*2-4)/2-6-1)/(2+24/(2+4))и выражений с переменными:

var a = 6;
var b = 4.32m;
var c = 24.15m;
var engine = new ExpressionEvaluator();
engine.Evaluate("(((9-a/2)*2-b)/2-a-1)/(2+c/(2+4))", new { a, b, c});

Вы также можете передавать параметры как именованные переменные:

dynamic dynamicEngine = new ExpressionEvaluator();

var a = 6;
var b = 4.5m;
var c = 2.6m;

dynamicEngine.Evaluate("(c+b)*a", a: 6, b: 4.5, c: 2.6);

Он поддерживает .Net Standard 2.0, поэтому может использоваться как из проектов .Net Core, так и из проектов .Net Full Framework и не имеет внешних зависимостей.


0

Бегите от быстрого оценщика экспрессии

https://flee.codeplex.com

Справочник по языку

  • Пример ArithmeticOperators: a * 2 + b ^ 2 - 100% 5
  • Пример ComparisonOperators: a <> 100
  • Пример AndOrXorNotOperators (логический): a> 100 And Not b = 100
  • ShiftOperators Пример: 100 >> 2
  • Пример конкатенации: «abc» + «def»
  • Пример индексации: arr [i + 1] + 100
  • Литералы
  • Пример кастинга: 100 + cast (obj, int)
  • Пример ConditionalOperator: If (a> 100 и b> 10, «оба больше», «меньше»)
  • Пример InOperator (список): If (100 в (100, 200, 300, -1), «в», «не в»)
  • Перегруженные операторы типов

Пример :

Imports Ciloci.Flee
Imports Ciloci.Flee.CalcEngine
Imports System.Math

    Dim ec As New Ciloci.Flee.ExpressionContext
    Dim ex As IDynamicExpression
    ec.Imports.AddType(GetType(Math))

    ec.Variables("a") = 10            
    ec.Variables("b") = 40               
    ex = ec.CompileDynamic("a+b")

    Dim evalData    
    evalData = ex.Evaluate()
    Console.WriteLine(evalData)

Выход: 50


0

MathNet. Символика

using System;
using static MathNet.Symbolics.SymbolicExpression;
using static System.Console;
using static System.Numerics.Complex;
using Complex = System.Numerics.Complex;

namespace MathEvaluator
{
    class Program
    {
        static readonly Complex i = ImaginaryOne;

        static void Main(string[] args)
        {
            var z = Variable("z");
            Func<Complex, Complex> f = Parse("z * z").CompileComplex(nameof(z));
            Complex c = 1 / 2 - i / 3;
            WriteLine(f(c));


            var x = Variable("x");
            Func<double, double> g = Parse("x * x + 5 * x + 6").Compile(nameof(x));
            double a = 1 / 3.0;
            WriteLine(g(a));
        }
    }
}

Не забудьте загрузить

<PackageReference Include="MathNet.Symbolics" Version="0.20.0" />
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.