在VB.NET中进行数学运算,类似于JavaScript中的Eval

5
有没有办法在vb.net中解析字符串(例如内置方法),可以像Eval一样进行数学计算?例如,将3 +(7/3.5)作为字符串返回2。
我不是要求你为我编写代码,我只想知道是否有内置的方法来做到这一点,如果没有,我会自己编写。
我可以打赌它无法自行解析Sin(90)之类的东西,我明白那需要用Math.Sin(90)替换。
如果有内置方法,如何使用它?

我没有答案。我不相信.NET语言中有这样的解析器(除非你按照Preet的建议去做)。如果你最终创建了一个解析器,请查看ANTLR。 - user1228
可能我得自己手写一个解析器了,对于基本的数学来说并不难。但是括号这个东西可能会有点棘手…… - Cyclone
对于括号,您可以使用递归。 - Robert L
6个回答

15

使用DataTable.Compute方法可以简化有限(即简单)的数学表达式。显然,这并不健壮(功能受限),而且滥用DataTable来实现这一目的可能会让人感到hackish,但我认为我应该补充当前答案。

示例:

var result = new DataTable().Compute("3+(7/3.5)", null); // 5

"

这种方法无法使用“Sin(90)”。请参阅DataColumn.Expression Property page以获取支持的函数列表,特别是在“聚合”部分下。

使用System.CodeDom命名空间是一种选择。

一些有用的链接:

"

编辑:为了回应您的评论,这里提供一种方法来演示使用其等效的 Math 类方法替换三角函数。

C#

string expression = "(Sin(0) + Cos(0)+Tan(0)) * 10";
string updatedExpression = Regex.Replace(expression, @"(?<func>Sin|Cos|Tan)\((?<arg>.*?)\)", match =>
            match.Groups["func"].Value == "Sin" ? Math.Sin(Int32.Parse(match.Groups["arg"].Value)).ToString() :
            match.Groups["func"].Value == "Cos" ? Math.Cos(Int32.Parse(match.Groups["arg"].Value)).ToString() :
            Math.Tan(Int32.Parse(match.Groups["arg"].Value)).ToString()
        );
var result = new DataTable().Compute(updatedExpression, null); // 10

VB.NET

Dim expression As String = "(Sin(0) + Cos(0)+Tan(0)) * 10"
Dim updatedExpression As String = Regex.Replace(expression, "(?<func>Sin|Cos|Tan)\((?<arg>.*?)\)", Function(match As Match) _
        If(match.Groups("func").Value = "Sin", Math.Sin(Int32.Parse(match.Groups("arg").Value)).ToString(), _
        If(match.Groups("func").Value = "Cos", Math.Cos(Int32.Parse(match.Groups("arg").Value)).ToString(), _
        Math.Tan(Int32.Parse(match.Groups("arg").Value)).ToString())) _
        )
Dim result = New DataTable().Compute(updatedExpression, Nothing)

请注意,您需要了解"arg"组的内容。我知道它们是整数,因此我在它们上面使用了Int32.Parse。如果它们是项的组合,则这种简单的方法将无法工作。我怀疑如果涉及到更多不支持的函数调用,则您将不断需要修补解决方案,在这种情况下,CodeDom方法或其他方法可能更适合。

1
它有效了,你刚刚帮我省了大量编码时间。我只需要按照之前的方式使用Sin和那种函数就可以了。这比我之前写的类要高效得多,哈哈。 - Cyclone

3

这里有一种评估表达式的方法,我在其他任何地方都没有看到过:使用 WebBrowser 控件和 JavaScript 的 eval() 方法:

Option Strict On

Imports System.Security.Permissions

<PermissionSet(SecurityAction.Demand, Name:="FullTrust")> _
<System.Runtime.InteropServices.ComVisibleAttribute(True)>
Public Class Form1
    Dim browser As New WebBrowser

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        browser.ObjectForScripting = Me
        'browser.ScriptErrorsSuppressed = True
        browser.DocumentText = "<script>function evalIt(x) { return eval(x); }</script>"
    End Sub

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click

        Dim result = browser.Document.InvokeScript("evalIt", New String() {"3+4*5"})
        If result IsNot Nothing Then
            MessageBox.Show(result.ToString())     '23
        End If
    End Sub
End Class

0

虽然有点晚了,但这正是您想要的(请确保解析输入以确保其中没有类似于“something + vbcrlf + exec“format c:”'的语句):

用法:

MessageBox.Show(COR.Tools.EvalProvider.Eval("return 8-3*2").ToString())

类:

'Imports Microsoft.VisualBasic
Imports System


Namespace COR.Tools


Public Class EvalProvider


    Public Shared Function Eval(ByVal vbCode As String) As Object
        Dim c As VBCodeProvider = New VBCodeProvider
        Dim icc As System.CodeDom.Compiler.ICodeCompiler = c.CreateCompiler()
        Dim cp As System.CodeDom.Compiler.CompilerParameters = New System.CodeDom.Compiler.CompilerParameters

        cp.ReferencedAssemblies.Add("System.dll")
        cp.ReferencedAssemblies.Add("System.xml.dll")
        cp.ReferencedAssemblies.Add("System.data.dll")
        ' Sample code for adding your own referenced assemblies
        'cp.ReferencedAssemblies.Add("c:\yourProjectDir\bin\YourBaseClass.dll")
        'cp.ReferencedAssemblies.Add("YourBaseclass.dll")
        cp.CompilerOptions = "/t:library"
        cp.GenerateInMemory = True
        Dim sb As System.Text.StringBuilder = New System.Text.StringBuilder("")
        sb.Append("Imports System" & vbCrLf)
        sb.Append("Imports System.Xml" & vbCrLf)
        sb.Append("Imports System.Data" & vbCrLf)
        sb.Append("Imports System.Data.SqlClient" & vbCrLf)
        sb.Append("Imports Microsoft.VisualBasic" & vbCrLf)

        sb.Append("Namespace MyEvalNamespace  " & vbCrLf)
        sb.Append("Class MyEvalClass " & vbCrLf)

        sb.Append("public function  EvalCode() as Object " & vbCrLf)
        'sb.Append("YourNamespace.YourBaseClass thisObject = New YourNamespace.YourBaseClass()")
        sb.Append(vbCode & vbCrLf)
        sb.Append("End Function " & vbCrLf)
        sb.Append("End Class " & vbCrLf)
        sb.Append("End Namespace" & vbCrLf)
        Debug.WriteLine(sb.ToString()) ' look at this to debug your eval string
        Dim cr As System.CodeDom.Compiler.CompilerResults = icc.CompileAssemblyFromSource(cp, sb.ToString())
        Dim a As System.Reflection.Assembly = cr.CompiledAssembly
        Dim o As Object
        Dim mi As System.Reflection.MethodInfo
        o = a.CreateInstance("MyEvalNamespace.MyEvalClass")
        Dim t As Type = o.GetType()
        mi = t.GetMethod("EvalCode")
        Dim s As Object
        s = mi.Invoke(o, Nothing)
        Return s
    End Function


End Class ' EvalProvider


End Namespace

0

它是在vb.net 2003中编写的,转换向导破坏了它(请参见http://stackoverflow.com/questions/1442025/visual-studio-conversion-wizard-why-put-it-in-if-it-is-broken-whats-the-point),所以除非你能为我编译成.dll文件,否则我无法使用它。谢谢你的尝试! - Cyclone
第二个链接呢?它实际上使用Javascript来为您完成工作。 - Robert Harvey
刚刚看到那个链接,如果我的定制解析器无法处理这份工作,我会使用它。我认为此刻我将首先尝试使用它,因为对我来说这将是一次很好的学习经验,因为我还是vb的新手。 - Cyclone
谢谢!当我失败时,至少我知道我将会有相同的结果。 - Cyclone
17
哦,亲爱的罗伯特。哦,亲爱的。 - user1228

0
一种方法是使用CodeDom命名空间编译它,并使用反射执行它。可能不是很高效,我不确定。

请参考Ahmad的回答中的CodeDom计算器。 - Malcolm

-1

我不知道VB.net是否内置了此功能,但我们通过链接IronPython运行时并传递表达式来实现这样的操作。这比使用反射要快得多。


抱歉,我误解了“嗯?”的意思,认为你是在问“IronPython是什么?”而实际上你是想表达一个更具体的问题。既然你已经知道IronPython是什么,那么你也应该知道它是一种动态语言(就像JavaScript),可以评估表达式(就像JavaScript)。你还必须知道它运行在.NET框架上。Preet Sangha的建议是,你可以制作一个简单的IronPython库来评估数学表达式,并从你的VB.NET代码中调用它。 - Joel Mueller
“嗯?”在提问时应该被解释为“什么是IronPython?”,正如您所正确地假设的那样,但是当您发布了解释如何搜索它的链接时,我已经这样做了,但是忘记了备注这一事实。请原谅我的疏忽,因为我现在生病了,忘记在完成搜索后立即检查问题。 - Cyclone

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接