มีผู้ประเมินคณิตศาสตร์สตริงใน. NET หรือไม่?


95

หากฉันมีสตริงที่มีนิพจน์ทางคณิตศาสตร์ที่ถูกต้องเช่น:

String s = "1 + 2 * 7";

มีไลบรารี / ฟังก์ชันในตัวใน. NET ที่จะแยกวิเคราะห์และประเมินนิพจน์นั้นให้ฉันและส่งคืนผลลัพธ์หรือไม่ ในกรณีนี้ 15.


2
ไม่ได้สร้างขึ้นในหนึ่งเดียว แต่มีความครอบคลุมสวยหนึ่งที่นี่
Strelok


1
คุณสามารถใช้ตัวประเมินนิพจน์ (ฟังก์ชัน Eval ใน. NET ที่มีการจัดการ 100%)
jadsc

ฉันเพิ่งสร้างวิธีแก้โค้ดอย่างเดียวเพื่อประเมินนิพจน์ทางคณิตศาสตร์ใน C # คุณสามารถดูรหัสที่blackbeltcoder.com/Articles/algorithms/ac-expression-evaluator
Jonathan Wood

ห้องสมุดนี้ดูเหมือนจะมีข้อบกพร่องบางอย่าง
Philippe Lavoie

คำตอบ:


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 คำนวณคำตอบด้านล่าง
dwilliss

64

แปลกที่คำถามที่มีชื่อเสียงและเก่าแก่นี้ไม่มีคำตอบที่ชี้ให้เห็นถึงDataTable.Compute"เคล็ดลับ" ในตัว นี่คือ.

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

ตัวดำเนินการทางคณิตศาสตร์ต่อไปนี้ได้รับการสนับสนุนในนิพจน์:

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

รายละเอียดเพิ่มเติม: DataColumn.Expressionที่แสดงออกไวยากรณ์


มีคำตอบจาก ma81xx เมื่อ 15 พ.ย. 54
tatigo

1
คุณพูดถูก แต่นั่นไม่ได้ใช้วิธีการคำนวณ
Tim Schmelter

ตัวอย่างของคุณให้ฉัน System.InvalidCastException ไม่สามารถจัดการได้โดยรหัสผู้ใช้ HResult = -2147467262 ฉันจะแก้ไขได้อย่างไร
Yuliia Ashomok

ได้ผลสำหรับฉันคุณเคยใช้โค้ดตัวอย่างนี้หรือไม่ ใช้ดีบักเกอร์และตรวจสอบค่าผลลัพธ์ประเภทที่กล่าวถึง
Tim Schmelter

28

สำหรับใครก็ตามที่พัฒนาใน C # บน Silverlight นี่เป็นเคล็ดลับที่ค่อนข้างเรียบร้อยที่ฉันเพิ่งค้นพบซึ่งช่วยให้สามารถประเมินนิพจน์ได้โดยเรียกใช้เครื่องมือ Javascript:

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

ฉันสงสัยว่าคุณสามารถอ้างอิงสิ่งนี้ที่อื่นได้หรือไม่ อาจจะไม่ แต่มันจะเย็น
Joel Coehoorn

4
เนื่องจากสิ่งนี้ประเมินโค้ด Javascript โดยพลการคุณอาจต้องการให้แน่ใจว่าได้ล้างข้อมูลที่คุณป้อนและตรวจสอบว่าคุณไม่ได้แสดงผลโดยตรง (ฉันคิดว่านี่จะเป็นวิธีที่ดีในการแนะนำ XSS โดยไม่รู้ตัว)
Dan Esparza

ลองป้อนตัวเลขด้วยศูนย์นำหน้าผลลัพธ์ไม่น่าเชื่อถือ "054 + 6" ให้ตัวอย่าง 50
Terry

9
@djerry นั่นเป็นเพราะตัวเลขที่มีศูนย์นำหน้าถือว่าเป็นฐานแปดโดยผู้ประเมินของ JS และเลขฐานแปดเท่ากับทศนิยม 44
André Leria

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

สังเกต% สูตร% ที่จะใส่นิพจน์

ในการคอมไพล์ให้ใช้คลาส CSharpCodeProvider ฉันไม่ต้องการใส่แหล่งที่มาที่สมบูรณ์ที่นี่ แต่คำตอบนี้อาจช่วยได้:

หลังจากที่คุณโหลดแอสเซมบลีหน่วยความจำแล้วคุณสามารถสร้างอินสแตนซ์ของคลาสของคุณและเรียกใช้ EvalCode


9

อีกทางเลือกหนึ่งที่ Roslyn พร้อมให้บริการแล้ว:

คุณสามารถใช้ไลบรารี 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();

พบเมื่อเร็ว ๆ นี้ - ในกรณีที่คุณต้องการลองใช้ไวยากรณ์ (และดูกรณีการใช้งานขั้นสูง) คุณสามารถดาวน์โหลดไฟล์ แอปScalar Calculator ที่ขับเคลื่อนโดย mXparser

ขอแสดงความนับถืออย่างสูง


นี่คือห้องสมุดที่ดีที่สุดที่ฉันพบ อย่างไรก็ตามไม่รองรับข้อมูลประเภทอื่นนอกจากตัวเลข! ฉันต้องการ DateTime และ String .. คุณรู้ทางเลือกที่ดีหรือไม่?
Homam

@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

ฉันจะดู Jace ด้วย ( https://github.com/pieterderycke/Jace ) Jace เป็นโปรแกรมแยกวิเคราะห์ทางคณิตศาสตร์และเครื่องมือคำนวณประสิทธิภาพสูงที่รองรับ. NET ทุกรสชาติ (.NET 4.x, Windows Phone, Windows Store, ... ) Jace มีให้บริการผ่าน NuGet: https://www.nuget.org/packages/Jace


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

ในการเปิดใช้งานลำดับความสำคัญโดยการยึดสแต็กของสแต็กจะเพียงพอเช่นเก็บถาวรโดยการเรียกซ้ำ สิ่งใดก็ตามที่อยู่ระหว่างวงเล็บจะถูกวางไว้ในสแต็กใหม่ ในที่สุดคุณก็สามารถสนับสนุนการดำเนินการทางคณิตศาสตร์ในรูปแบบที่อ่านง่ายโดย lambdas


คุณอาจต้องการยืนยันคำตอบของคุณ 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: รวมสิ่งนี้เข้ากับคำตอบ @cbp มีศูนย์ที่ต้องมีคำตอบสองคำที่เหมือนกันโดยพื้นฐานเมื่อเราสามารถมีคำตอบที่ยอดเยี่ยมได้หนึ่งคำตอบ
Robert MacLean

1

ฉันใช้โปรแกรมแยกวิเคราะห์นิพจน์เมื่อไม่กี่ปีที่ผ่านมาและได้เผยแพร่เวอร์ชันหนึ่งในGitHubและNuget: Albatross.Expressionเมื่อเร็ว ๆ นี้ ประกอบด้วยคลาส ExecutionContext ที่สามารถประเมินชุดของนิพจน์เช่น:

  • MV = ราคา * จำนวน;
  • ราคา = (Bid + Ask) / 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

Flee Fast Lightweight Expression Evaluator

https://flee.codeplex.com

การอ้างอิงภาษา

  • ArithmeticOperators ตัวอย่าง: a * 2 + b ^ 2 - 100% 5
  • ตัวดำเนินการเปรียบเทียบตัวอย่าง: a <> 100
  • AndOrXorNotOperators ตัวอย่าง (ตรรกะ): a> 100 และไม่ใช่ b = 100
  • ShiftOperators ตัวอย่าง: 100 >> 2
  • ตัวอย่างการเชื่อมต่อ: "abc" + "def"
  • ตัวอย่างการจัดทำดัชนี: arr [i + 1] + 100
  • ตัวอักษร
  • ตัวอย่างการร่าย: 100 + cast (obj, int)
  • ConditionalOperator ตัวอย่าง: If (a> 100 and b> 10, "both greater", "less")
  • ตัวอย่าง InOperator (รายการ): ถ้า (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" />
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.