可能重复:
我需要一个快速的运行时表达式解析器
当有人在我页面上的文本框中输入x*y^z以计算后面代码中的方程式并获得结果时,我该如何做到这一点?
可能重复:
我需要一个快速的运行时表达式解析器
当有人在我页面上的文本框中输入x*y^z以计算后面代码中的方程式并获得结果时,我该如何做到这一点?
使用内置的 .NET 功能,用户https://stackoverflow.com/users/1670022/matt-crouch将操作员作为字符串回答:
“如果你只需要简单的算术,那就这样做吧。
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上运行它
.NET 没有用于评估任意字符串的内置函数。但是,一个名为NCalc的开源 .NET 库可以。
NCalc 是 .NET 中的数学表达式求值器。NCalc 可以解析任何表达式并评估结果,包括静态或动态参数和自定义函数。
这个问题有两种主要方法,每种方法都有一些变化,如各种答案所示。
在详细介绍这一点之前,有必要强调解释任意数学表达式并非易事,对于“玩具”语法以外的任何表达式语法,例如只接受一两个算术运算并且不允许括号等等
理解这样的任务是微不足道的,并承认,毕竟,解释平均复杂度的算术表达式是各种应用程序的相对经常性需求[因此应该有成熟的解决方案],尝试和凑合可能是明智的与“选项 A”。
因此,我赞同 Jed 对现成表达式评估器(例如NCalc )的建议。
然而,花时间去理解与解析和解释算术表达式相关的各种概念和方法可能会很有用,就好像一个人要开始自己的实现一样。
关键概念是形式语法的概念。评估者将接受的算术表达式必须遵循一组规则,例如允许的算术运算列表。例如,评估器是否支持三角函数,或者如果支持,这是否也包括atan2()。规则还指出了操作数的构成,例如是否允许输入大到 45 位的数值。等等。关键是所有这些规则都以语法形式化了。
通常,语法适用于先前从原始输入文本中提取的标记。本质上在过程中的某个时间,一些逻辑需要逐个字符地分析输入字符串,并确定哪些字符序列一起出现。例如,在123 + 45 / 9.3
表达式中,标记是整数值123
、plus
运算符、整数值45
、division
运算符,最后是9.3
实际值。识别标记并将它们与标记类型相关联的任务是词法分析器的工作. 词法分析器可以建立在语法(“标记”是单个字符的语法,与算术表达式解析器的语法相反,它的标记是词法分析器生成的短字符串。)
顺便说一句,语法用于定义算术表达式之外的许多其他事物。计算机语言遵循 [相当复杂的] 语法,但引入领域特定语言(也称为 DSL)以支持计算机应用程序的各种功能是相对常见的。
对于非常简单的语法,也许可以从头开始编写相应的词法分析器和解析器。但迟早,语法可能会变得复杂到手写这些模块变得挑剔、容易出错并且可能更重要的是难以阅读。因此,Lexer 和 Parser Generators的存在是独立的程序,它们从规则列表(以特定于生成器的语法表示,尽管许多生成器倾向于使用类似的语法,但松散地基于BNF)。
当使用这样的词法分析器/解析器生成器时,工作分多个步骤完成:
- 第一个编写语法定义(在生成器特定的语言/语法中)
- 通过生成器运行此语法。
- 经常多次重复上述两个步骤,因为编写语法是一项严格的练习:生成器会抱怨许多可能的歧义,人们可能会在语法中写入。
- 最终生成器生成一个源文件(使用所需的目标语言,如 C# 等)
- 此源包含在整个项目中
- 项目中的其他源文件可能会调用由生成器生成的源文件中公开的函数和/或与在解析期间识别的各种模式相对应的一些逻辑可以很容易地嵌入到生成器生成的代码中。
- 然后可以像往常一样构建项目,即好像解析器和词法分析器是手写的。
这就是关于使用正式语法和代码生成器的过程的 20,000 英尺高的演示。
可以在此链接中找到解析器生成器(又名编译器编译器)的列表。对于 C# 中的简单工作,我还想提一下Irony。细读这些网站可能会非常有见地,以更好地了解这些概念,即使此时没有成为从业者的意图。
如前所述,我想强调的是,对于这个特定的应用程序,现成的算术评估器可能是更好的方法。这些的主要缺点是
用这个库解决了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
你需要的——如果你想自己做的话——是一个扫描器(也称为词法分析器) +解析器在解释表达式的代码中。或者,您可以找到一个 3rd 方库,它可以完成这项工作并且与 JavaScripteval(string)
函数的工作方式相似。
请看这里,它描述了一个递归下降解析器。该示例是用C编写的,但是一旦您掌握了文章中描述的想法,您应该能够轻松地将其适应C# 。
它没有听起来那么复杂,特别是如果您要支持的操作员数量有限。
优点是您可以完全控制将执行的表达式(以防止您网站的最终用户注入恶意代码)。
您将需要实现(或查找第三方来源)表达式解析器。这不是一件小事。