从C#转换过来的表达式解析器,求大神是否能支持中文混合。
Public Class Calculator''' <summary>最高运算级别常量</summary>
Protected Const MAX_LEVEL As Integer = 99
''' <summary> ''' 获取运算符的级别 ''' </summary> ''' <param name="o"></param> ''' <returns></returns>
Private Shared Function GetOperatorLevel(ByVal o As String) As Integer
For i As Integer = 0 To Level.Length - 1
If DirectCast(Level(i)(0), String) = o Then
Return CInt(Level(i)(1))
End If
Next
Return -1
End Function
''' <summary> ''' 如果字符串是已运算符开头,则返回该运算符,否则返回null ''' </summary> ''' <param name="v">要检查的字符串</param> ''' <returns></returns>
Private Shared Function GetOperator(ByVal v As String) As String
For i As Integer = 0 To Level.Length - 1
If v.StartsWith(DirectCast(Level(i)(0), String)) Then
Return DirectCast(Level(i)(0), String)
End If
Next
Return Nothing
End Function
#Region "运算符与支持的函数" '数学函数 '三角函数 '条件函数
Private Shared Level As Object()() = New Object()() _
{ _
New Object() {",", 0}, New Object() {"=", 1}, New Object() {">=", 1}, New Object() {"<=", 1}, New Object() {"<>", 1}, New Object() {">", 1}, New Object() {"<", 1}, New Object() {"+", 2}, New Object() {"-", 2}, New Object() {"*", 3}, New Object() {"/", 3}, New Object() {"%", 3}, New Object() {"NEG", 4}, New Object() {"^", 5}, _
New Object() {"(", MAX_LEVEL}, _
New Object() {"ROUND(", MAX_LEVEL}, _
New Object() {"TRUNC(", MAX_LEVEL}, _
New Object() {"MAX(", MAX_LEVEL}, _
New Object() {"MIN(", MAX_LEVEL}, _
New Object() {"ABS(", MAX_LEVEL}, _
New Object() {"SUM(", MAX_LEVEL}, _
New Object() {"AVERAGE(", MAX_LEVEL}, _
New Object() {"SQRT(", MAX_LEVEL}, _
New Object() {"EXP(", MAX_LEVEL}, _
New Object() {"LOG(", MAX_LEVEL}, _
New Object() {"LOG10(", MAX_LEVEL}, _
New Object() {"SIN(", MAX_LEVEL}, _
New Object() {"COS(", MAX_LEVEL}, _
New Object() {"TAN(", MAX_LEVEL}, _
New Object() {"IF(", MAX_LEVEL}, _
New Object() {"NOT(", MAX_LEVEL}, _
New Object() {"AND(", MAX_LEVEL}, _
New Object() {"OR(", MAX_LEVEL} _
}
#End Region
#Region "解析表达式"
''' <summary>运算符</summary>
Private _opt As String = Nothing
''' <summary>运算符右边的表达式</summary>
Private _expression As String = Nothing
''' <summary>运算符左边的值</summary>
Private _leftValue As String
Public Shared Function CalculateExpression(ByVal expression As String, ByVal dataProvider As Specialized.NameValueCollection) As Decimal()
Dim calc As New Calculator(expression, dataProvider)
Dim r As Decimal() = calc.Calculate()
Return r
End Function
Private _data As Specialized.NameValueCollection
Public Sub New(ByVal expression As String, ByVal dataProvider As Specialized.NameValueCollection)
_data = dataProvider
_expression = expression.ToUpper()
If GetIndex(_expression) <> -1 Then
Throw New Exception("缺少""(""")
End If
Initialize()
End Sub
''' <summary>初始化对象(将表达式拆分为左边的值、运算符和右边的表达式)</summary>
Private Sub Initialize()
Dim right As String
GetNext(_expression, _leftValue, _opt, right)
_expression = right
End Sub
''' <summary> ''' 拆分表达式 ''' </summary> ''' <param name="expression">表达式</param> ''' <param name="left">左边的值</param> ''' <param name="opt">运算符</param> ''' <param name="right">右边的值</param>
Private Sub GetNext(ByVal expression As String, ByRef left As String, ByRef opt As String, ByRef right As String)
right = expression
left = String.Empty
opt = Nothing
While right <> String.Empty
opt = GetOperator(right)
If opt IsNot Nothing Then
right = right.Substring(opt.Length, right.Length - opt.Length)
Exit While
Else : left += right(0)
right = right.Substring(1, right.Length - 1)
End If
End While
right = right.Trim()
End Sub
''' <summary>计算表达式</summary> ''' <returns>返回值(可能有多个,如逗号表达式会返回多个值)</returns>
Public Function Calculate() As Decimal()
'如果运算符为空,则直接返回左边的值
If _opt Is Nothing Then
Dim r As Decimal = Decimal.Zero
Try
r = Decimal.Parse(_leftValue)
Catch
Try
r = Decimal.Parse(_data(_leftValue))
Catch
Throw New Exception("错误的格式:" & _leftValue)
End Try
End Try
Return New Decimal(0) {r}
End If
'判断是否是最高优先级的运算符(括号和函数)
If GetOperatorLevel(_opt) <> MAX_LEVEL Then
'四则运算符中,只有当-左边无值的时候是单目运算
If _opt <> "-" AndAlso _leftValue = String.Empty Then
Throw New Exception("""" & _opt & """运算符的左边需要值或表达式")
End If
If _opt = "-" AndAlso _leftValue = String.Empty Then
_opt = "NEG"
End If
If _expression = String.Empty Then
Throw New Exception("""" & _opt & """运算符的右边需要值或表达式")
End If
Return CalculateTwoParms()
Else '括号和函数左边都不需要值
If _leftValue <> String.Empty Then
Throw New Exception("""" & _opt & """运算符的左边不需要值或表达式")
End If
Return CalculateFunction()
End If
End Function
''' <summary>计算函数(括号运算符被当作函数计算,所有的函数必须已右括号结尾)</summary>
Private Function CalculateFunction() As Decimal()
'查找对应的右括号
Dim inx As Integer = GetIndex(_expression)
If inx = -1 Then
Throw New Exception("缺少')'")
End If
Dim l As String = _expression.Substring(0, inx)
'如果表达式已经完成,则返回计算结果,否则计算当前结果
'并修改左值、运算符、右边表达式的值,然后调用Calculate继续运算
If inx = _expression.Length - 1 Then
Return Calc(_opt, l)
Else
Dim left As String, right As String, op As String
_expression = _expression.Substring(inx + 1, _expression.Length - inx - 1)
GetNext(_expression, left, op, right)
Dim r As Decimal() = Calc(_opt, l)
_leftValue = r(r.Length - 1).ToString()
If op Is Nothing Then
Throw New Exception("')'运算符的右边需要运算符")
End If
_opt = op
_expression = right
Return Calculate()
End If
End Function
''' <summary> ''' 获取第一个未封闭的右括号在字符串中的位置 ''' </summary>
''' <param name="expression">传入的字符串</param>
Private Function GetIndex(ByVal expression As String) As Integer
Dim count As Integer = 0
For i As Integer = 0 To expression.Length - 1
If expression(i) = ")" Then
If count = 0 Then
Return i
Else
count -= 1
End If
End If
If expression(i) = "(" Then
count += 1
End If
Next
Return -1
End Function
''' <summary>计算四则表达式</summary>
Private Function CalculateTwoParms() As Decimal()
Dim left As String, right As String, op As String
GetNext(_expression, left, op, right)
Dim result As Decimal(), r As Decimal()
'如果下一个运算符的级别不大于当前运算符,则计算当前的值
If op Is Nothing OrElse (GetOperatorLevel(_opt) >= GetOperatorLevel(op)) Then
r = Calc(_opt, _leftValue, left)
Else
'如果下一个运算符的级别大于当前运算符
Dim ex As String = left
'则一直找到低于当前运算符级别的运算符,然后将该运算符和当前运算符中间的表达式 '提取出来,新构造一个对象,运算中 间级别高的表达式的值()
'然后将新对爱的结果当作右边的值于当前的左值以及运算符进行运算
While (GetOperatorLevel(_opt) < GetOperatorLevel(op) AndAlso right <> String.Empty)
ex += op
If GetOperatorLevel(op) = MAX_LEVEL Then
Dim pos As Integer = GetIndex(right)
ex += right.Substring(0, pos + 1)
right = right.Substring(pos + 1)
End If
GetNext(right, left, op, right)
ex += left
End While
Dim calc__1 As New Calculator(ex, _data)
Dim rl As Decimal() = calc__1.Calculate()
r = Calc(_opt, _leftValue, rl(rl.Length - 1).ToString())
End If
'将上一步计算出来的结果作为当前的左值,然后将表达式剩下的部分作为当前的右边的表达式 '然后将下一个运算符作为当前运 算符, 然后递归运算
_leftValue = r(r.Length - 1).ToString()
_opt = op
_expression = right
Dim rr As Decimal() = Calculate()
result = New Decimal(r.Length - 1 + (rr.Length - 1)) {}
For i As Integer = 0 To r.Length - 2
result(i) = r(i)
Next
For i As Integer = 0 To rr.Length - 1
result(r.Length - 1 + i) = rr(i)
Next
Return result
End Function
#End Region
从C#转换过来的表达式解析器 --------------------编程问答-------------------- #Region "运算"
Private Function Calc(ByVal opt As String, ByVal expression As String) As Decimal()
Dim calc__1 As New Calculator(expression, _data)
Dim values As Decimal() = calc__1.Calculate()
Dim v As Decimal = values(values.Length - 1)
Dim r As Decimal = Decimal.Zero
Select Case _opt
Case "("
r = v
Exit Select
Case "Str("
r = v
Exit Select
Case "ROUND("
If values.Length > 2 Then
Throw New Exception("Round函数需要一个或两个参数!")
End If
If values.Length = 1 Then
r = Decimal.Round(v, 0)
Else
r = Decimal.Round(values(0), CInt(Math.Truncate(values(1))))
End If
Exit Select
Case "TRUNC("
If values.Length > 1 Then
Throw New Exception("Trunc函数只需要一个参数!")
End If
r = Decimal.Truncate(v)
Exit Select
Case "MAX("
If values.Length < 2 Then
Throw New Exception("Max函数至少需要两个参数!")
End If
r = values(0)
For i As Integer = 1 To values.Length - 1
If values(i) > r Then
r = values(i)
End If
Next
Exit Select
Case "MIN("
If values.Length < 2 Then
Throw New Exception("Min函数至少需要两个参数!")
End If
r = values(0)
For i As Integer = 1 To values.Length - 1
If values(i) < r Then
r = values(i)
End If
Next
Exit Select
Case "ABS"
If values.Length > 1 Then
Throw New Exception("Abs函数只需要一个参数!")
End If
r = Math.Abs(v)
Exit Select
Case "SUM("
For Each d As Decimal In values
r += d
Next
Exit Select
Case "AVERAGE("
For Each d As Decimal In values
r += d
Next
r /= values.Length
Exit Select
Case "IF("
If values.Length <> 3 Then
Throw New Exception("IF函数需要三个参数!")
End If
If GetBoolean(values(0)) Then
r = values(1)
Else
r = values(2)
End If
Exit Select
Case "NOT("
If values.Length <> 1 Then
Throw New Exception("NOT函数需要一个参数!")
End If
If GetBoolean(values(0)) Then
r = 0
Else
r = 1
End If
Exit Select
Case "OR("
If values.Length < 1 Then
Throw New Exception("OR函数至少需要两个参数!")
End If
For Each d As Decimal In values
If GetBoolean(d) Then
Return New Decimal(0) {1}
End If
Next
Exit Select
Case "AND("
If values.Length < 1 Then
Throw New Exception("AND函数至少需要两个参数!")
End If
For Each d As Decimal In values
If Not GetBoolean(d) Then
Return New Decimal(0) {Decimal.Zero}
End If
Next
r = 1
Exit Select
Case "SQRT("
If values.Length <> 1 Then
Throw New Exception("SQRT函数需要一个参数!")
End If
r = CDec(Math.Sqrt(CDbl(v)))
Exit Select
Case "SIN("
If values.Length <> 1 Then
Throw New Exception("Sin函数需要一个参数!")
End If
r = CDec(Math.Sin(CDbl(v)))
Exit Select
Case "COS("
If values.Length <> 1 Then
Throw New Exception("Cos函数需要一个参数!")
End If
r = CDec(Math.Cos(CDbl(v)))
Exit Select
Case "TAN("
If values.Length <> 1 Then
Throw New Exception("Tan函数需要一个参数!")
End If
r = CDec(Math.Tan(CDbl(v)))
Exit Select
Case "EXP("
If values.Length <> 1 Then
Throw New Exception("Exp函数需要一个参数!")
End If
r = CDec(Math.Exp(CDbl(v)))
Exit Select
Case "LOG("
If values.Length > 2 Then
Throw New Exception("Log函数需要一个或两个参数!")
End If
If values.Length = 1 Then
r = CDec(Math.Log(CDbl(v)))
Else
r = CDec(Math.Log(CDbl(values(0)), CDbl(values(1))))
End If
Exit Select
Case "LOG10("
If values.Length <> 1 Then
Throw New Exception("Log10函数需要一个参数!")
End If
r = CDec(Math.Log10(CDbl(v)))
Exit Select
End Select
Return New Decimal(0) {r}
End Function
Private Function GetBoolean(ByVal d As Decimal) As Boolean
Return CInt(Math.Truncate(d)) = 1
End Function
Private Function Calc(ByVal opt As String, ByVal leftEx As String, ByVal rightEx As String) As Decimal()
Dim r As Decimal = Decimal.Zero
Dim left As Decimal = Decimal.Zero, right As Decimal
Try
left = Decimal.Parse(leftEx)
Catch
If opt <> "NEG" Then
Try
left = Decimal.Parse(_data(leftEx))
Catch
Throw New Exception("错误的格式:" & leftEx)
End Try
End If
End Try
Try
right = Decimal.Parse(rightEx)
Catch
Try
right = Decimal.Parse(_data(rightEx))
Catch
Throw New Exception("错误的格式:" & leftEx)
End Try
End Try
Select Case _opt
Case "NEG"
r = Decimal.Negate(right)
Exit Select
Case "+"
r = left + right
Exit Select
Case "-"
r = left - right
Exit Select
Case "*"
r = left * right
Exit Select
Case "/"
r = left / right
Exit Select
Case "%"
r = Decimal.Remainder(left, right)
Exit Select
Case "^"
r = CDec(Math.Pow(CDbl(left), CDbl(right)))
Exit Select
Case ","
Return New Decimal(1) {left, right}
Case "="
r = IIf(left = right, 1, 0)
Exit Select
Case "<>"
r = IIf(left <> right, 1, 0)
Exit Select
Case "<"
r = IIf(left < right, 1, 0)
Exit Select
Case ">"
r = IIf(left > right, 1, 0)
Exit Select
Case ">="
r = IIf(left >= right, 1, 0)
Exit Select
Case "<="
r = IIf(left <= right, 1, 0)
Exit Select
End Select
Return New Decimal(0) {r}
End Function
#End Region
End Class --------------------编程问答-------------------- 这段代码能解析 IF(2>1,1,0) 这样的表达式。
但我们在应用中更需要的是能够像EXCEL 一样,解析 IF(3>2,"是","否")
更进一步我需要从表达式: IF(3>2,"最高限额为:" +2000,0) 能获得结果: "最高限额为:2000"
补充:.NET技术 , VB.NET