.NET中是否有字符串数学评估器?


93

如果我有一个带有有效数学表达式的字符串,例如:

String s = "1 + 2 * 7";

.NET中是否有内置库/函数可以为我解析和评估该表达式并返回结果?在这种情况下15。


2
不是内置的。但是有一个非常全面的一个位置
Strelok,


1
您可以使用表达式求值器(100%托管.NET中的Eval函数)
jadsc 2010年

我刚刚创建了一个仅代码的解决方案来评估C#中的数学表达式。您可以在blackbeltcoder.com/Articles/algorithms/ac-expression-evaluator上查看代码。
乔纳森·伍德

该库似乎有一些错误。
菲利普·拉沃

Answers:


53

您可以添加对Microsoft脚本控制库(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.Compute答案。
dwilliss

62

奇怪的是,这个著名而古老的问题没有一个暗示内置DataTable.Compute“技巧” 的答案。这里是。

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

表达式中支持以下算术运算符:

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

更多信息:DataColumn.Expression表达式语法


有一个从ma81xx年11月15回答'11
tatigo

1
您是对的,但是没有使用Compute方法。
蒂姆·施梅尔特

您的示例给了我System.InvalidCastException未由用户代码HResult = -2147467262处理。如何解决它?
Yuliia Ashomok,2015年

它对我有用,您使用过此示例代码吗?使用调试器并检查结果值,其中提到了类型。
蒂姆·施密特

28

对于任何在Silverlight上用C#开发的人来说,这是一个我发现的巧妙技巧,它允许通过调用Javascript引擎来评估表达式:

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

我想知道您是否可以在其他地方引用此内容。可能不是,但这很酷。
乔尔·科恩荷恩

4
由于这会评估任意Javascript代码,因此您可能希望确保清除输入内容,并确保不直接显示结果。(我认为这是引入XSS的一种不错的方法,但是没有意识到它)
Dan Esparza

尝试输入前导零的数字,结果不可靠。例如,“ 054 + 6”为您50。
特里

9
@djerry,这是因为带有前导零的数字被JS的评估认为八进制,八进制和054等号十进制44
安德烈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); }
    ...

请注意将在其中放置表达式的%formula%。

要进行编译,请使用类CSharpCodeProvider。我不想在这里输入完整的源代码。但是这个答案可能会有所帮助:

加载内存中的程序集后,您可以创建类的实例并调用EvalCode。


9

现在有了罗斯林的另一个选择:

您可以为此使用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();

最近找到的-如果您想尝试语法(并查看高级用例),则可以下载由mXparser支持的Scalar Calculator 应用程序

最好的祝福


这是我发现的最好的图书馆。但是,它不支持数字以外的任何数据类型!我需要DateTime和String ..您知道其他好的选择吗?
Homam '18

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

一个简单的数学解析器很容易构建,并且只需要几行代码:

举一个灵活的例子:

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答案。当我们可以得到一个奇妙的答案时,有零个需要基本相同的答案。
罗伯特·麦克莱恩

1

几年前,我实现了一个表达式解析器,并在GitHubNuget:Albatross.Expression中发布了它的一个版本。它包含一个ExecutionContext类,该类可以评估一组表达式,例如:

  • MV =价格*数量;
  • 价格=(买价+卖价)/ 2;
  • 出价= 0.6;
  • 问= 0.8;

它还内置了循环引用检查,这对于避免堆栈溢出很有用。


1

您可以使用我作为作者的Math-Expression-Evaluator库。它支持简单的表达式,如2.5+5.917.89-2.47+7.165/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

逃离快速轻量级表达评估器

https://flee.codeplex.com

语言参考

  • ArithmeticOperators示例:a * 2 + b ^ 2-100%5
  • 比较运算符示例:<> 100
  • AndOrXorNotOperators示例(逻辑):a> 100而不是b = 100
  • ShiftOperators示例:100 >> 2
  • 串联示例:“ abc” +“ def”
  • 索引示例:arr [i + 1] + 100
  • 文字
  • 强制转换示例:100 + cast(obj,int)
  • ConditionalOperator示例:If(a> 100且b> 10,“两者均较大”,“较小”)
  • InOperator示例(列表):If(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" />
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.