Якщо у мене є рядок з дійсним математичним виразом, наприклад:
String s = "1 + 2 * 7";
Чи існує в .NET вбудована бібліотека / функція, яка буде аналізувати та обчислювати цей вираз для мене та повертати результат? У цьому випадку 15.
Якщо у мене є рядок з дійсним математичним виразом, наприклад:
String s = "1 + 2 * 7";
Чи існує в .NET вбудована бібліотека / функція, яка буде аналізувати та обчислювати цей вираз для мене та повертати результат? У цьому випадку 15.
Відповіді:
Ви можете додати посилання на 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" та виберіть "ОК".
Дивно, що це відоме і давнє запитання не має відповіді, яка б вказувала на вбудований DataTable.Compute- "фокус". Ось.
double result = Convert.ToDouble(new DataTable().Compute("1 + 2 * 7", null));
У виразах підтримуються такі арифметичні оператори:
+ (addition)
- (subtraction)
* (multiplication)
/ (division)
% (modulus)
Додаткова інформація: DataColumn.Expressionу Синтаксисі виразів .
Для тих, хто розробляє в C # на Silverlight, ось щойно виявив досить акуратний трюк, який дозволяє оцінювати вираз, викликаючи механізм Javascript:
double result = (double) HtmlPage.Window.Eval("15 + 35");
Ви бачили 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 для завантаження нових функцій.
Насправді є якийсь вбудований - ви можете використовувати простір імен 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);
}
Спочатку я використовував обгортку 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.
Ще один варіант, коли Рослин доступний:
Для цього ви можете використовувати бібліотеку 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" />
Нещодавно я використовував mXparser, який є бібліотекою математичного аналізатора для .NET та JAVA. mXparser підтримує основні формули, а також дуже вигадливі / складні (включаючи змінні, функції, оператори, ітерацію та рекурсію).
https://mxparser.codeplex.com/
Кілька прикладів використання:
Приклад 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.
З найкращими побажаннями
Якщо вам потрібна дуже проста річ, ви можете використовувати 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")
Я б також подивився Джейса ( https://github.com/pieterderycke/Jace ). Jace - це високопродуктивний математичний аналізатор та механізм обчислень, який підтримує всі варіанти .NET (.NET 4.x, Windows Phone, Windows Store, ...). Джейс також доступний через NuGet: https://www.nuget.org/packages/Jace
Простий математичний синтаксичний аналізатор досить простий у побудові і вимагає лише декількох рядків коду:
Візьмемо цей гнучкий приклад:
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
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
}
}
}
Я впровадив парсер виразів кілька років тому і нещодавно опублікував його версію в GitHub та Nuget: Albatross.Expression . Він містить клас ExecutionContext, який може оцінити набір таких виразів, як:
Він також має вбудовану кругову перевірку посилань, що корисно, щоб уникнути переповнення стека.
Ви можете використовувати бібліотеку 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, і він не має жодних зовнішніх залежностей.
Утікайте від швидкого полегшеного оцінювача виразів
Мовна довідка
Приклад:
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
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" />