.NET'te bir dize matematik değerlendiricisi var mı?


95

Aşağıdaki gibi geçerli bir matematik ifadesine sahip bir dizem varsa:

String s = "1 + 2 * 7";

NET'te bu ifadeyi benim için ayrıştırıp değerlendirecek ve sonucu döndürecek yerleşik bir kitaplık / işlev var mı? Bu durumda 15.


2
Yerleşik değil. Ancak burada oldukça kapsamlı bir tane var .
Strelok


1
İfade değerlendiricisini kullanabilirsiniz (% 100 yönetilen .NET'te Değerlendirme işlevi)
jadsc

C # 'ta matematiksel ifadeleri değerlendirmek için yalnızca kod içeren bir çözüm geliştirdim. Kodu blackbeltcoder.com/Articles/algorithms/ac-expression-evaluator adresinde görebilirsiniz .
Jonathan Wood

Bu kütüphanede bazı hatalar var gibi görünüyor.
Philippe Lavoie

Yanıtlar:


53

Microsoft Komut Dosyası Kontrol Kitaplığı'na (COM) bir başvuru ekleyebilir ve bir ifadeyi değerlendirmek için bunun gibi bir kod kullanabilirsiniz. (JScript için de çalışır.)

Dim sc As New MSScriptControl.ScriptControl()
sc.Language = "VBScript"
Dim expression As String = "1 + 2 * 7"
Dim result As Double = sc.Eval(expression)

Düzenle - C # sürümü.

MSScriptControl.ScriptControl sc = new MSScriptControl.ScriptControl();
sc.Language = "VBScript";
string expression = "1 + 2 * 7";
object result = sc.Eval(expression);            
MessageBox.Show(result.ToString());

Düzenle - ScriptControl bir COM nesnesidir. Projenin "Referans ekle" iletişim kutusunda "COM" sekmesini seçin ve "Microsoft Script Control 1.0" a gidin ve ok'u seçin.


2
Cevap olarak bu işaretlenmiş olmasına rağmen, 10 yıl önceydi ve COM artık bir nevi ölü durumda. Aşağıdaki DataTable.Compute yanıtını tercih ediyorum.
dwilliss

64

Bu ünlü ve eski sorunun yerleşik olan DataTable.Compute"hile" yi akla getiren bir cevabı olmaması garip . İşte burada.

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

Aşağıdaki aritmetik operatörler ifadelerde desteklenir:

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

Daha Fazla Bilgi: DataColumn.ExpressionEn İfade Sözdizimi .


15 Kasım 2011'de ma81xx'den bir cevap geldi
tatigo

1
Haklısın, ama bu Compute yöntemini kullanmıyordu.
Tim Schmelter

Örneğiniz bana System.InvalidCastException kullanıcı kodu tarafından işlenmedi HResult = -2147467262 veriyor Nasıl düzeltebilirim?
Yuliia Ashomok

Benim için çalışıyor, bu örnek kodu kullandınız mı? Hata ayıklayıcıyı kullanın ve sonuç değerini inceleyin, türden bahsedilir.
Tim SCHMELTER

28

Silverlight üzerinde C # ile geliştirme yapan herkes için işte, Javascript motoruna seslenerek bir ifadenin değerlendirilmesine izin verdiğini keşfettiğim oldukça temiz bir numara:

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

Acaba buna başka bir yerden başvurabilir misiniz? Muhtemelen hayır, ama harika olurdu.
Joel Coehoorn

4
Bu rastgele Javascript kodunu değerlendirdiğinden, muhtemelen girdinizi temizlediğinizden ve sonucu doğrudan görüntülemediğinizden emin olmak istersiniz. (Bunun XSS'yi fark etmeden tanıtmanın iyi bir yolu olacağını düşünüyorum)
Dan Esparza

Başında sıfır olan sayılar girmeyi deneyin, sonuç güvenilir değildir. Örneğin "054 + 6" size 50 verir.
Terry

9
@djerry, bunun nedeni, başında sıfır olan sayıların JS'nin değerlendiricisi tarafından sekizlik olarak kabul edilmesidir ve sekizlik 054, ondalık 44'e eşittir.
André Leria

24

Http://ncalc.codeplex.com'u gördünüz mü ?

Genişletilebilir, hızlıdır (örneğin kendi önbelleğine sahiptir), EvaluateFunction / EvaluateParameter olaylarını işleyerek çalışma zamanında özel işlevler ve değişkenler sağlamanıza olanak tanır. Ayrıştırabileceği örnek ifadeler:

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

Ayrıca, unicode ve birçok veri türünü yerel olarak işler. Dilbilgisini değiştirmek istiyorsanız, bir boynuz dosyası ile birlikte gelir. Yeni işlevleri yüklemek için MEF'i destekleyen bir çatal da vardır.


1
Harika kütüphane. Ayrıca NUGET
Paul Grimshaw'da

Bu, bir kullanıcının girdisini alan diferansiyel denklem çözücüm için kullandığım şeydi. Soru burada
kleineg

15

Aslında bir tür yerleşik bir tane var - XPath ad alanını kullanabilirsiniz! XPath gösterimi ile onaylamak için dizeyi yeniden biçimlendirmenizi gerektirse de. Basit ifadeleri işlemek için buna benzer bir yöntem kullandım:

    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

Başlangıçta muparser için c # sarmalayıcı kullandım . Bu çok hızlıydı. Bildiğim tek hızlı çözüm exprtk . Başka çözümler arıyorsanız, karşılaştırmayı kontrol edebilirsiniz .

Ancak .Net söz konusu olduğunda, çalışma zamanında kodu derlemek için yerleşik desteği kullanabilirsiniz. Buradaki fikir, örneğin değerlendirme formülünü değiştirebileceğiniz gömülü kaynak olarak bir "şablon" kaynak dosyasına sahip olmaktır. Daha sonra bu hazırlanmış sınıf kaynak kodunu derleyiciye iletirsiniz.

Temel bir şablon şöyle görünebilir:

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

İfadenin yerleştirileceği% formülüne dikkat edin.

Derlemek için CSharpCodeProvider sınıfını kullanın. Kaynağın tamamını buraya koymak istemiyorum. Ancak bu cevap yardımcı olabilir:

Bellek içi derlemeyi yükledikten sonra, sınıfınızın bir örneğini oluşturabilir ve EvalCode'u çağırabilirsiniz.


9

Roslyn'in mevcut olduğu başka bir seçenek daha:

Bunun için CodeAnalysis.CSharp.Scripting kütüphanesini kullanabilirsiniz.

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

<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
Cevap şimdi bu olmalı
sonofaforester

8

Son zamanlarda .NET ve JAVA için matematik ayrıştırıcı kitaplığı olan mXparser kullanıyordum. mXparser temel formüllerin yanı sıra çok süslü / karmaşık olanları (değişkenler, işlevler, operatörler, yineleme ve özyineleme dahil) destekler.

https://mxparser.codeplex.com/

https://mathparser.org/

Birkaç kullanım örneği:

Örnek 1:

Expression e = new Expression("1+2*7 + (sin(10) - 2)/3");
double v = e.calculate();

Örnek 2:

Argument x = new Argument("x = 5");
Expression e = new Expression("2*x+3", x);
double v = e.calculate();

Örnek 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();

Yakın zamanda bulundu - sözdizimini denemek (ve gelişmiş kullanım örneğini görmek) isterseniz, mXparser tarafından desteklenen Skaler Hesap Makinesi uygulamasını indirebilirsiniz.

Saygılarımla


Bulduğum en iyi kütüphane bu. Ancak sayı dışında herhangi bir veri türünü desteklemez! DateTime ve String'e ihtiyacım var .. iyi bir alternatif biliyor musunuz?
Homam

@Homam String işlemleri için bir alternatif buldunuz mu
Ramakrishna Reddy

5

Çok basit bir şeye ihtiyacınız varsa 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

Basit bir matematik ayrıştırıcısının oluşturulması oldukça kolaydır ve yalnızca birkaç satır kod gerektirir:

Bu esnek örneği ele alalım:

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

Özyineleme ile arşivlenenler gibi bir yığın yığınını parantez içine alarak önceliği etkinleştirmek yeterli olacaktır. Parantezler arasında herhangi bir şey yeni bir yığına yerleştirilir. Son olarak, matematik işlemlerini lambdas tarafından okunabilir bir şekilde destekleyebilirsiniz.


Cevabınızı doğrulamak isteyebilirsiniz. 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: Bunu @cbp cevabıyla birleştirmeniz yeterli. Tek bir harika cevaba sahip olduğumuzda temelde aynı olan iki cevaba SIFIR ihtiyaç vardır.
Robert MacLean

1

Birkaç yıl önce bir ifade ayrıştırıcısı uyguladım ve yakın zamanda GitHub ve Nuget: Albatross.Expression'da bunun bir sürümünü yayınladım . Aşağıdakiler gibi bir dizi ifadeyi değerlendirebilen bir ExecutionContext sınıfı içerir:

  • MV = Fiyat * Miktar;
  • Fiyat = (Teklif + Sor) / 2;
  • Teklif = .6;
  • Sor = .8;

Ayrıca, yığın taşmasını önlemek için yararlı olan yerleşik dairesel referans denetimine sahiptir.


1

Benim yazarı olduğum Math-Expression-Evaluator kütüphanesini kullanabilirsiniz . Çok basit ifadeleri destekler gibi 2.5+5.9, 17.89-2.47+7.16, 5/2/2+1.5*3+4.58, parantez ile ifadeleri (((9-6/2)*2-4)/2-6-1)/(2+24/(2+4))değişkenlerle ve ifadelerin:

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

Parametreleri adlandırılmış değişkenler olarak da iletebilirsiniz:

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'ı destekler, böylece .Net Core ve .Net Full Framework projelerinden kullanılabilir ve herhangi bir harici bağımlılığı yoktur.


0

Flee Fast Lightweight İfade Değerlendiricisi

https://flee.codeplex.com

Dil referansı

  • Aritmetik İşlemciler Örneği: a * 2 + b ^ 2 -% 100 5
  • ComparisonOperators Örneği: a <> 100
  • AndOrXorNotOperators Örneği (mantıksal): a> 100 ve b = 100 değil
  • ShiftOperators Örneği: 100 >> 2
  • Birleştirme Örneği: "abc" + "def"
  • İndeksleme Örneği: arr [i + 1] + 100
  • Değişmezler
  • Döküm Örneği: 100 + cast (obj, int)
  • ConditionalOperator Örneği: If (a> 100 ve b> 10, "her ikisi de büyük", "daha az")
  • InOperator Örneği (Liste): If (100 inç (100, 200, 300, -1), "inç", "değil")
  • Türlerde Aşırı Yüklenmiş Operatörler

Misal :

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)

Çıktı: 50


0

MathNet.Sembolikler

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

Yüklemeyi unutma

<PackageReference Include="MathNet.Symbolics" Version="0.20.0" />
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.