【问题标题】:Convert Expression from a Textbox to Math Expression in Code Behind [duplicate]在后面的代码中将表达式从文本框转换为数学表达式 [重复]
【发布时间】:2012-10-25 15:18:40
【问题描述】:

可能重复:
I need a fast runtime expression parser

当有人在我页面上的文本框中键入 x*y^z 以计算后面代码中的方程式并得到结果时,我如何做到这一点?

【问题讨论】:

标签: c# math equation equations


【解决方案1】:

.NET 没有用于评估任意字符串的内置函数。但是,一个名为 NCalc 的开源 .NET 库可以。

NCalc 是 .NET 中的数学表达式求值器。 NCalc 可以解析 任何表达式并评估结果,包括静态或动态 参数和自定义函数。

【讨论】:

  • ' 很惊讶你的建议没有轻易地渗透到顶部。我最好尽自己的一份力量(+1),因为我将这种方法插入到我自己的“宣言”中
  • 注意: NCalc 已移至 Github:github.com/sheetsync/NCalc,不再维护。答案中的(存档)链接建议查看github.com/sebastienros/jint,该链接仍在继续开发中。
【解决方案2】:

用户https://stackoverflow.com/users/1670022/matt-crouch 使用内置.NET 功能从operators as strings 回答:

“如果你只需要简单的算术,就这样做吧。

    DataTable temp = new DataTable();
    Console.WriteLine(temp.Compute("15 / 3",string.Empty));

编辑:更多信息。查看 System.Data.DataColumn 类的 Expression 属性的 MSDN 文档。 “表达式语法”上的内容概述了除算术运算符之外您可以使用的命令列表。 (例如 IIF、LEN 等)。”

编辑 2:为方便起见,您可以将其放入一个小函数中,例如:

public string Eval(string expr)
{
    var temp = new System.Data.DataTable();
    string result = null;
    try
    {           
        result = $"{temp.Compute(expr, string.Empty)}"; 
    }
    catch (System.Data.EvaluateException ex)
    {
      if (ex.Message.ToLower().Contains("cannot find column"))
            throw new System.Data.SyntaxErrorException($"Syntax error: Invalid expression: '{expr}'."
                                           + " Variables as operands are not supported.");
      else
            throw;
    }
    
    return result;
}

所以你可以像这样使用它:

Console.WriteLine(Eval("15 * (3 + 5) / (7 - 2)"));

给出预期的输出:

24

注意,错误处理程序有助于处理由于使用此处不允许的变量而导致的异常。示例:Eval("a") - 不是返回 "Cannot find column [a]",这在此上下文中没有多大意义(我们没有在数据库上下文中使用它),而是返回 "Syntax error: Invalid expression: 'a'. Variables as operands are not supported."

DotNetFiddle上运行它

【讨论】:

    【解决方案3】:

    解决这个问题有两种主要方法,每种方法都有一些变化,如各种答案所示。

    • 选项 A:查找现有的数学表达式求值器
    • 选项 B:编写自己的解析器和计算结果的逻辑

    在详细介绍之前,有必要强调解释任意数学表达式不是一项简单的任务,对于“玩具”语法以外的任何表达式语法,例如只接受一两个算术运算,不允许括号等。

    了解这样的任务看似微不足道,并承认解释平均复杂度的算术表达式毕竟是各种应用程序的相对经常性需求[因此应该有成熟的解决方案],尝试一下可能是明智的并使用“选项 A”。
    因此,我赞同 Jed 对现成表达式评估器的建议,例如 NCalc

    不过,花点时间了解与解析和解释算术表达式相关的各种概念和方法可能会很有用,就好像你要自己动手做一样。

    关键概念是formal grammar。评估者将接受的算术表达式必须遵循一组规则,例如允许的算术运算列表。例如,评估器是否支持三角函数,或者如果支持,这是否也包括 atan2()。规则还指出了操作数的构成,例如是否允许输入大到 45 位的数值。等等。关键是所有这些规则都以语法形式化了。

    通常,语法适用于之前从原始输入文本中提取的 标记。本质上在过程中的某个时间,一些逻辑需要分析输入字符串,逐个字符,并确定哪些字符序列一起出现。例如,在123 + 45 / 9.3 表达式中,标记是整数值123plus 运算符、整数值45division 运算符,最后是9.3 实际值。识别令牌并将它们与令牌类型相关联的任务是lexer 的工作。词法分析器可以建立在语法(“标记”是单个字符的语法,与算术表达式解析器的语法相反,它的标记是词法分析器生成的短字符串。)

    顺便说一句,语法用于定义算术表达式之外的许多其他事物。计算机语言遵循 [相当复杂的] 语法,但引入 Domain Specific Languages 又名 DSL 以支持计算机应用程序的各种功能是相对常见的。

    对于非常简单的语法,也许可以从头开始编写相应的词法分析器和解析器。但迟早语法可能会变得复杂到手写这些模块变得挑剔、容易出错并且可能更重要的是难以阅读。因此,Lexer and Parser Generators 的存在是独立的程序,它们从规则列表中生成词法分析器和解析器的代码(使用特定的编程语言,如 C、Java 或 C#)(以生成器特有的语法表示,尽管许多生成器倾向于使用类似的语法,但大致基于 BNF)。

    当使用这样的词法分析器/解析器生成器时,分多个步骤完成工作:
    - 第一个编写语法定义(使用生成器特定的语言/语法)
    - 通过生成器运行此语法。
    - 人们经常多次重复上述两个步骤,因为编写语法是一项严格的练习:生成器会抱怨许多可能的歧义,人们可能会在语法中写入。
    - 最终生成器生成一个源文件(使用所需的目标语言,例如 C# 等)
    - 此来源包含在整个项目中
    - 项目中的其他源文件可能会调用生成器生成的源文件中公开的函数和/或与解析期间识别的各种模式相对应的一些逻辑可能很容易嵌入到生成器生成的代码中。
    - 然后可以像往常一样构建项目,即好像解析器和词法分析器是手写的。

    这就是 20,000 英尺高的演示使用正式语法和代码生成器的过程。
    可以在 link 找到解析器生成器(又名编译器编译器)的列表。对于 C# 中的简单工作,我还想提一下 Irony。细读这些网站可能会非常有见地,以便更好地了解这些概念,即使此时没有成为从业者的意图。
    如前所述,我想强调对于这个特定的应用程序,现成的算术评估器可能是更好的方法。这些的主要缺点是

    • 关于允许的表达式语法的一些限制(允许的语法过于严格:您还需要说 stddev() 或过于宽泛:您不希望您的用户使用三角函数。随着更成熟评估者,将会有某种形式的配置/扩展功能来处理这个问题。
    • 此类第 3 方模块的学习曲线。希望其中许多应该是相对“即插即用”的。

    【讨论】:

      【解决方案4】:

      用这个库解决了http://www.codeproject.com/Articles/21137/Inside-the-Mathematical-Expressions-Evaluator

      我的最终代码

      Calculator Cal = new Calculator();
      txt_LambdaNoot.Text = (Cal.Evaluate(txt_C.Text) / fo).ToString();
      

      现在当某个类型 3*10^11 他将得到 300000000000

      【讨论】:

        【解决方案5】:

        您需要实现(或查找第三方来源)表达式解析器。这不是一件小事。

        【讨论】:

          【解决方案6】:

          如果你想自己做的话,你需要的是代码中的 Scanner (also known as Lexer) + Parser 来解释表达式。或者,您可以找到一个与 JavaScript eval(string) 函数类似的第 3 方库。

          请看一下here,它描述了一个递归下降解析器。该示例是用 C 编写的,但是一旦您掌握了文章中描述的想法,您应该能够轻松地将其改编为 C#

          它并不像听起来那么复杂,尤其是在您需要支持的运算符数量有限的情况下。

          优点是您可以完全控制将要执行的表达式(以防止您网站的最终用户注入恶意代码)。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2020-12-15
            • 2017-05-01
            • 1970-01-01
            • 2013-02-11
            • 1970-01-01
            • 1970-01-01
            • 2023-03-11
            相关资源
            最近更新 更多