Чи існує в .NET обчислювач математичних рядків?


95

Якщо у мене є рядок з дійсним математичним виразом, наприклад:

String s = "1 + 2 * 7";

Чи існує в .NET вбудована бібліотека / функція, яка буде аналізувати та обчислювати цей вираз для мене та повертати результат? У цьому випадку 15.


2
Не вбудований. Але є досить всебічний один тут .
Strelok


1
Ви можете використовувати оцінювач виразів (функція Eval у 100% керованому .NET)
jadsc

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

Здається, у цій бібліотеці є деякі помилки.
Філіп Лавуа

Відповіді:


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у Синтаксисі виразів .


була відповідь від ma81xx 15 листопада '11
татіго

1
Ви маєте рацію, але це було не за допомогою методу Compute.
Тім Шмельтер

Ваш приклад дає мені System.InvalidCastException був оброблений кодом користувача HResult = -2147467262 Як я можу це виправити?
Юлія Ашомок

Це працює для мене, чи використовували ви цей зразок коду? Використовуйте налагоджувач та перевіряйте значення результату, згаданий тип.
Тім Шмельтер

28

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

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

Цікаво, чи можете ви посилатися на це деінде. Можливо, ні, але було б круто.
Joel Coehoorn

4
Оскільки це обчислює довільний код Javascript, ви, мабуть, хочете бути впевненим, що дезінфікуєте свої дані та переконайтесь, що ви не відображаєте результат безпосередньо. (Я вважаю, це був би хороший спосіб запровадити XSS, не усвідомлюючи цього)
Ден Еспарза,

Спробуйте ввести числа з початковим нулем, результат не є надійним. "054 + 6" дає вам 50, наприклад.
Террі,

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
Paul Grimshaw

Це те, що я використав для свого розв'язувача диференціальних рівнянь, який взяв введення даних користувача. Питання тут
kleineg

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

Ще один варіант, коли Рослин доступний:

Для цього ви можете використовувати бібліотеку 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 Чи знайшли ви альтернативу для струнних операцій
Ramakrishna Reddy

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

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

Візьмемо цей гнучкий приклад:

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.1проти391
Тавані

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, який може оцінити набір таких виразів, як:

  • МВ = Ціна * Кількість;
  • Ціна = (ставка + запит) / 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

Мовна довідка

  • Приклад арифметичних операторів: a * 2 + b ^ 2 - 100% 5
  • ComparisonOperators Приклад: a <> 100
  • Приклад AndOrXorNotOperators (логічний): a> 100 А не b = 100
  • Приклад ShiftOperators: 100 >> 2
  • Приклад об'єднання: "abc" + "def"
  • Приклад індексування: arr [i + 1] + 100
  • Літерали
  • Приклад кастингу: 100 + акторський склад (obj, int)
  • Приклад умовного оператора: Якщо (a> 100 і b> 10, "обидва більші", "менше")
  • Приклад InOperator (Список): If (100 in (100, 200, 300, -1), "in", "not in")
  • Перевантажені оператори на типи

Приклад:

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.