5

有什么方法可以解析 vb.net 中的字符串(例如,内置方法),可以像 Eval 一样进行数学运算吗?例如,3+(7/3.5) 作为字符串将返回 2。

我不是要你为我编写代码,我只是想知道是否有内置的方法可以做到这一点,如果没有,我会自己编写代码。

我可以打赌它自己无法解析像 Sin(90) 这样的东西,我知道需要用 Math.Sin(90) 替换它。

如果有内置方法,你如何使用它?

4

6 回答 6

15

通过使用DataTable.Compute 方法,有一个用于有限(即简单)数学表达式的快捷方式。显然,这并不健壮(功能有限),并且为此目的滥用 DataTable 感觉很糟糕,但我想我会添加到当前的答案中。

例子:

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

“Sin(90)”不适用于这种方法。有关受支持函数的列表,请参阅DataColumn.Expression 属性页面,特别是在“聚合”部分下。

使用 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 方法或其他方法可能更合适。

于 2009-09-21T00:01:47.547 回答
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
于 2014-09-14T10:11:06.327 回答
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
于 2010-08-07T09:16:08.470 回答
0

一种方法是使用 CodeDom 命名空间来编译它并使用反射执行它。我不知道,可能没有那么高效。

于 2009-09-20T23:36:23.197 回答
0

这篇 CodeProject 文章可能会奏效:

用 VB.NET 编写的表达式求值器
http://www.codeproject.com/KB/vb/expression_evaluator.aspx

还有这个: http:
//www.dotnetspider.com/resources/2518-mathematical-expression-expression-evaluate-VB.aspx

于 2009-09-20T23:18:14.713 回答
-1

我不知道内置的 VB.net,但我们通过在 IronPython 运行时链接并将表达式传递给它来做这样的事情。这比使用反射要快得多。

于 2009-09-20T23:12:54.100 回答