Nếu tôi có một chuỗi với biểu thức toán học hợp lệ, chẳng hạn như:
String s = "1 + 2 * 7";
Có thư viện / hàm tích hợp sẵn trong .NET sẽ phân tích cú pháp và đánh giá biểu thức đó cho tôi và trả về kết quả không? Trong trường hợp này 15.
Nếu tôi có một chuỗi với biểu thức toán học hợp lệ, chẳng hạn như:
String s = "1 + 2 * 7";
Có thư viện / hàm tích hợp sẵn trong .NET sẽ phân tích cú pháp và đánh giá biểu thức đó cho tôi và trả về kết quả không? Trong trường hợp này 15.
Câu trả lời:
Bạn có thể thêm tham chiếu đến Thư viện điều khiển tập lệnh của Microsoft (COM) và sử dụng mã như thế này để đánh giá một biểu thức. (Cũng hoạt động cho JScript.)
Dim sc As New MSScriptControl.ScriptControl()
sc.Language = "VBScript"
Dim expression As String = "1 + 2 * 7"
Dim result As Double = sc.Eval(expression)
Chỉnh sửa - Phiên bản C #.
MSScriptControl.ScriptControl sc = new MSScriptControl.ScriptControl();
sc.Language = "VBScript";
string expression = "1 + 2 * 7";
object result = sc.Eval(expression);
MessageBox.Show(result.ToString());
Chỉnh sửa - ScriptControl là một đối tượng COM. Trong hộp thoại "Thêm tham chiếu" của dự án chọn tab "COM" và cuộn xuống "Microsoft Script Control 1.0" và chọn ok.
Lạ là câu hỏi nổi tiếng và lâu đời này lại không có câu trả lời mà gợi ý cho kẻ dựng DataTable.Compute- "lừa". Nó đây.
double result = Convert.ToDouble(new DataTable().Compute("1 + 2 * 7", null));
Các toán tử số học sau được hỗ trợ trong các biểu thức:
+ (addition)
- (subtraction)
* (multiplication)
/ (division)
% (modulus)
Thông tin thêm: DataColumn.Expressiontại Cú pháp biểu thức .
Đối với bất kỳ ai đang phát triển bằng C # trên Silverlight, đây là một thủ thuật khá đơn giản mà tôi vừa khám phá ra cho phép đánh giá một biểu thức bằng cách gọi đến công cụ Javascript:
double result = (double) HtmlPage.Window.Eval("15 + 35");
Bạn đã xem http://ncalc.codeplex.com chưa?
Nó có thể mở rộng, nhanh chóng (ví dụ: có bộ đệm riêng) cho phép bạn cung cấp các chức năng tùy chỉnh và các biến thể tại thời điểm chạy bằng cách xử lý các sự kiện AssessFunction / AssessParameter. Biểu thức ví dụ nó có thể phân tích cú pháp:
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());
Nó cũng xử lý unicode và nhiều kiểu dữ liệu nguyên bản. Nó đi kèm với một tệp antler nếu bạn muốn thay đổi máy cắt. Ngoài ra còn có một nhánh hỗ trợ MEF tải các chức năng mới.
Trên thực tế, có một loại được tích hợp sẵn - bạn có thể sử dụng không gian tên XPath! Mặc dù nó yêu cầu bạn định dạng lại chuỗi để xác nhận với ký hiệu XPath. Tôi đã sử dụng một phương thức như thế này để xử lý các biểu thức đơn giản:
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);
}
Ban đầu tôi sử dụng trình bao bọc c # cho muparser . Điều này rất nhanh. Giải pháp nhanh hơn duy nhất mà tôi biết là exprtk . Nếu bạn đang tìm kiếm các giải pháp khác, bạn có thể kiểm tra điểm chuẩn .
Nhưng trong trường hợp .Net, bạn có thể sử dụng hỗ trợ nội trang để biên dịch mã trong thời gian chạy. Ý tưởng là có một tệp nguồn "mẫu", ví dụ như tài nguyên nhúng nơi bạn có thể thay thế công thức cho đánh giá. Sau đó, bạn chuyển mã nguồn-lớp đã chuẩn bị này vào trình biên dịch.
Một mẫu cơ bản có thể trông như thế này:
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); }
...
Chú ý đến% công thức% nơi biểu thức sẽ được đưa vào.
Để biên dịch, hãy sử dụng lớp CSharpCodeProvider. Tôi không muốn đưa vào nguồn hoàn chỉnh ở đây. Nhưng câu trả lời này có thể giúp:
Sau khi bạn đã tải lắp ráp trong bộ nhớ, bạn có thể tạo một phiên bản của lớp và gọi EvalCode.
Tuy nhiên, một tùy chọn khác hiện có sẵn Roslyn:
Bạn có thể sử dụng thư viện CodeAnalysis.CSharp.Scripting cho việc này.
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();
}
}
}
gói 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" />
Gần đây tôi đã sử dụng mXparser, là một thư viện phân tích cú pháp toán học cho .NET và JAVA. mXparser hỗ trợ các công thức cơ bản cũng như các công thức rất phức tạp / phức tạp (bao gồm các biến, hàm, toán tử, lặp và đệ quy).
https://mxparser.codeplex.com/
Một vài ví dụ sử dụng:
Ví dụ 1:
Expression e = new Expression("1+2*7 + (sin(10) - 2)/3");
double v = e.calculate();
Ví dụ 2:
Argument x = new Argument("x = 5");
Expression e = new Expression("2*x+3", x);
double v = e.calculate();
Ví dụ 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();
Được tìm thấy gần đây - trong trường hợp bạn muốn thử cú pháp (và xem trường hợp sử dụng nâng cao), bạn có thể tải xuống ứng dụng Máy tính vô hướng được cung cấp bởi mXparser.
Trân trọng
Nếu bạn cần một thứ rất đơn giản, bạn có thể sử dụng 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")
Tôi cũng sẽ xem qua Jace ( https://github.com/pieterderycke/Jace ). Jace là một công cụ tính toán và phân tích cú pháp toán hiệu suất cao hỗ trợ tất cả các phiên bản .NET (.NET 4.x, Windows Phone, Windows Store, ...). Jace cũng có sẵn thông qua NuGet: https://www.nuget.org/packages/Jace
Một trình phân tích cú pháp toán học đơn giản khá dễ tạo và chỉ cần một vài dòng mã:
Lấy ví dụ linh hoạt này:
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);
Để kích hoạt quyền ưu tiên bằng cách đặt chéo một ngăn xếp sẽ đủ, chẳng hạn như được lưu trữ bằng đệ quy. Bất kỳ thứ gì giữa các dấu ngoặc đều được đặt trên một ngăn xếp mới. Cuối cùng, bạn có thể hỗ trợ các phép toán theo kiểu dễ đọc bởi lambdas.
100.5 + 300.5 - 100 * 10 / 100 = 30.1vs391
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
}
}
}
Tôi đã triển khai một trình phân tích cú pháp biểu thức một vài năm trước và đã xuất bản một phiên bản của nó trong GitHub và Nuget: Albatross.Expression gần đây. Nó chứa một lớp ExecutionContext có thể đánh giá một tập hợp các biểu thức như:
Nó cũng có tích hợp kiểm tra tham chiếu vòng tròn rất hữu ích để tránh tràn ngăn xếp.
Bạn có thể sử dụng thư viện Math-Expression-Assessuator mà tôi là tác giả. Nó hỗ trợ biểu thức đơn giản như 2.5+5.9, 17.89-2.47+7.16, 5/2/2+1.5*3+4.58, biểu thức với dấu ngoặc đơn (((9-6/2)*2-4)/2-6-1)/(2+24/(2+4))và biểu thức với các biến:
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});
Bạn cũng có thể chuyển các tham số dưới dạng các biến được đặt tên:
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);
Nó hỗ trợ .Net Standard 2.0 nên có thể được sử dụng từ .Net Core cũng như các dự án .Net Full Framework và nó không có bất kỳ phụ thuộc bên ngoài nào.
Flee Công cụ đánh giá biểu thức nhanh nhẹ
Ngôn ngữ tham khảo
Thí dụ :
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)
Đầu ra: 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));
}
}
}
Đừng quên tải
<PackageReference Include="MathNet.Symbolics" Version="0.20.0" />