Doing math in vb.net like Eval in javascript

后端 未结 6 953

Is there any way to parse a string in vb.net (like, built in methods), that can do math like Eval can? For example, 3+(7/3.5) as a string would return 2.

I am not as

相关标签:
6条回答
  • 2020-11-28 15:13

    I don't know about VB.net built in, but we do stuff like this by linking in the IronPython runtime and passing it the expressions. This is way way faster that using reflection.

    0 讨论(0)
  • 2020-11-28 15:16

    This CodeProject article might do the trick:

    An expression evaluator written in VB.NET
    http://www.codeproject.com/KB/vb/expression_evaluator.aspx

    There is also this:
    http://www.dotnetspider.com/resources/2518-mathematical-expression-expression-evaluate-VB.aspx

    0 讨论(0)
  • 2020-11-28 15:22

    There's a shortcut for limited (ie. simple) math expressions by using the DataTable.Compute method. Obviously, this isn't robust (limited functionality) and feels hackish to misuse the DataTable for this purpose, but I figured I would add to the current answers.

    Example:

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

    "Sin(90)" wouldn't work with this approach. Refer to the DataColumn.Expression Property page for a list of supported functions, specifically under the "Aggregates" section.

    Using the System.CodeDom namespace is an option.

    Some helpful links:

    • CodeDom Calculator
    • Evaluating mathematical expressions using CodeDom
    • Related SO question: Is there a string math evaluator in .NET?

    EDIT: to address your comment, here is an approach to demonstrate replacing trigonometric functions with their equivalent Math class methods.

    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)
    

    Note, however, that you need to know the contents of the "arg" group. I know they are ints, so I used Int32.Parse on them. If they are a combination of items then this simple approach won't work. I suspect you will constantly need to band-aid the solution if it gets too complicated with more unsupported function calls, in which case the CodeDom approach or others may be more suitable.

    0 讨论(0)
  • 2020-11-28 15:28

    It's a bit late, but here is exactly what you wanted (make sure you parse the input to make sure there is no statement like 'something + vbcrlf + exec "format c:"' in it):

    Usage:

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

      Class:

    '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 讨论(0)
  • 2020-11-28 15:29

    One way would be to use the CodeDom namespace to compile it and execute it using reflection. May not be that performant, I don't know.

    0 讨论(0)
  • 2020-11-28 15:30

    Here is a way to evaluate an expression that I haven't seen mentioned anywhere else: use a WebBrowser control and JavaScript's 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 讨论(0)
提交回复
热议问题