Có trình đánh giá toán học chuỗi trong .NET không?


95

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.


2
Không phải là một xây dựng trong một. Nhưng có một cái khá toàn diện ở đây .
— Strelok


1
Bạn có thể sử dụng Người đánh giá biểu hiện (chức năng Eval 100% được quản lý NET)
— jadsc

Tôi vừa tạo một giải pháp chỉ có mã để đánh giá các biểu thức toán học trong C #. Bạn có thể xem mã tại blackbeltcoder.com/Articles/algorithm/ac-expression-evaluator .
— Jonathan Wood

Thư viện này dường như có một số lỗi.
— Philippe Lavoie

Câu trả lời:


53

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.


2
Mặc dù đây được đánh dấu là câu trả lời, nhưng nó đã 10 năm trước và COM bây giờ đã chết. Tôi thích câu trả lời DataTable.Compute bên dưới.
— dwilliss

64

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 .


đã có câu trả lời từ ma81xx vào ngày 15 tháng 11 '11
— tatigo

1
Bạn đúng, nhưng đó không phải là sử dụng phương pháp Tính toán.
— Tim Schmelter

Ví dụ của bạn cho tôi biết System.InvalidCastException bị mã người dùng HResult = -2147467262 xử lý bằng cách nào?
— Yuliia Ashomok,

Nó hoạt động cho tôi, bạn đã sử dụng mã mẫu này chưa? Sử dụng trình gỡ lỗi và kiểm tra giá trị kết quả, loại được đề cập.
— Tim Schmelter

28

Đố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");

Tôi tự hỏi nếu bạn có thể tham khảo điều này ở nơi khác. Có lẽ là không, nhưng sẽ rất tuyệt.
— Joel Coehoorn

4
Vì điều này đánh giá mã Javascript tùy ý, có thể bạn muốn đảm bảo làm sạch đầu vào của mình và đảm bảo rằng bạn không trực tiếp hiển thị kết quả. (Tôi sẽ nghĩ rằng đây sẽ là một cách tốt để giới thiệu XSS mà không nhận ra nó)
— Dan Esparza

Hãy thử nhập các số có số 0 ở đầu, kết quả không đáng tin cậy. Ví dụ: "054 + 6" cho bạn 50.
— Terry

9
@djerry, đó là vì các số có số 0 đứng đầu được trình đánh giá của JS coi là hệ bát phân và số bát phân 054 bằng số thập phân 44.
— André Leria 13/12/12

24

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.


1
Thư viện tuyệt vời. Cũng có trên NUGET
— Paul Grimshaw

Đây là những gì tôi đã sử dụng cho công cụ giải phương trình vi phân của mình, lấy đầu vào của người dùng. Câu hỏi ở đây
— kleineg

15

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

12

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.


9

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" />

2
Điều này sẽ là câu trả lời tại
— sonofaforester

8

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/

https://mathparser.org/

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


Đây là thư viện tốt nhất mà tôi tìm thấy. Tuy nhiên, nó không hỗ trợ bất kỳ kiểu dữ liệu nào ngoài số! Tôi cần DateTime và String .. bạn có biết bất kỳ sự thay thế tốt nào không?
— Homam

@Homam Bạn có tìm thấy giải pháp thay thế cho các hoạt động chuỗi không
— Ramakrishna Reddy

5

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


3

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.


Bạn có thể muốn xác minh câu trả lời của mình. 100.5 + 300.5 - 100 * 10 / 100 = 30.1vs391
— Tawani

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: Chỉ cần hợp nhất câu trả lời này thành câu trả lời @cbp. Không cần thiết phải có hai câu trả lời về cơ bản giống hệt nhau khi chúng ta có thể có một câu trả lời tuyệt vời.
— Robert MacLean

1

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ư:

  • MV = Giá * Số lượng;
  • Giá = (Giá + Hỏi) / 2;
  • Giá thầu = .6;
  • Hỏi = .8;

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.


1

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.


0

Flee Công cụ đánh giá biểu thức nhanh nhẹ

https://flee.codeplex.com

Ngôn ngữ tham khảo

  • ArithmeticOperators Ví dụ: a * 2 + b ^ 2 - 100% 5
  • ComparisonOperators Ví dụ: a <> 100
  • Ví dụ về AndOrXorNotOperators (logic): a> 100 Và không phải b = 100
  • ShiftOperator Ví dụ: 100 >> 2
  • Ví dụ về nối: "abc" + "def"
  • Ví dụ lập chỉ mục: arr [i + 1] + 100
  • Chữ viết
  • Ví dụ truyền: 100 + cast (obj, int)
  • Ví dụ về ConditionalOperator: Nếu (a> 100 và b> 10, "cả lớn hơn", "nhỏ hơn")
  • Ví dụ về InOperator (Danh sách): If (100 in (100, 200, 300, -1), "in", "not in")
  • Các toán tử quá tải về các loại

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


0

MathNet.Symbolics

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" />
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookie và Chính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.