539 lines
21 KiB
VB.net
539 lines
21 KiB
VB.net
Imports System.Text
|
||
|
||
Namespace Expression
|
||
''' <summary>
|
||
''' 字符算数表达式求和
|
||
''' </summary>
|
||
Public Class StringExpression
|
||
''' <summary>精度</summary>
|
||
Private Shared _doublePrecision As Double = 0.000001
|
||
|
||
|
||
''' <summary>
|
||
''' 替换占位符
|
||
''' </summary>
|
||
''' <param name="str">字符串表达式</param>
|
||
''' <returns></returns>
|
||
Public Shared Function UseMarkSymbolReplace(str As String) As String
|
||
Dim destString As New StringBuilder
|
||
str = str.Replace(" ", "")
|
||
|
||
str = str.Replace(">>", "r") 'right move
|
||
str = str.Replace("<<", "l") ' left move
|
||
str = str.Replace("||", "o") 'or
|
||
str = str.Replace("&&", "a") 'and
|
||
str = str.Replace(">=", "g") 'greater equal
|
||
str = str.Replace("<=", "s") 'small equal
|
||
str = str.Replace("<>", "u") 'unequal
|
||
str = str.Replace("!=", "u") 'unequal
|
||
str = str.Replace("==", "e") 'equal
|
||
|
||
|
||
For i As Integer = 0 To str.Length - 1 '替换负号
|
||
If i = 0 Then
|
||
If str.Chars(i) = "-"c Then
|
||
destString.Append("0"c)
|
||
End If
|
||
destString.Append(str.Chars(i))
|
||
ElseIf str.Chars(i) = "-"c AndAlso str.Chars(i - 1) = "("c Then
|
||
destString.Append("0"c)
|
||
destString.Append(str.Chars(i))
|
||
Else
|
||
destString.Append(str.Chars(i))
|
||
End If
|
||
Next
|
||
Return destString.ToString()
|
||
End Function
|
||
|
||
''' <summary>
|
||
''' 替换表达式式中的十六进制占位符为实际内容,例如替换B4为buf中的第4位即buf(4)的值
|
||
''' </summary>
|
||
''' <param name="buf"></param>
|
||
''' <param name="expressionString"></param>
|
||
''' <returns></returns>
|
||
Public Shared Function ReplaceBytes(buf() As Byte, expressionString As String) As String
|
||
Dim ch As Char
|
||
Dim count As Integer = 0
|
||
Dim str As String = ""
|
||
Dim tmp As Integer
|
||
For i As Integer = 0 To expressionString.Length - 1
|
||
If expressionString.Chars(i) = "B"c Then
|
||
ch = expressionString.Chars(i + count + 1)
|
||
While IsNumeric(ch)
|
||
count += 1
|
||
If i + count + 1 >= expressionString.Length Then Exit While
|
||
ch = expressionString.Chars(i + count + 1)
|
||
End While
|
||
If count <= 0 Then Return ""
|
||
Dim st As String = Mid(expressionString, i + 1 + 1, count)
|
||
tmp = Convert.ToInt32(st)
|
||
If tmp > buf.Length - 1 Then Return ""
|
||
str += buf(tmp).ToString
|
||
i += count
|
||
count = 0
|
||
Else
|
||
str += expressionString.Chars(i)
|
||
End If
|
||
Next
|
||
Return str
|
||
End Function
|
||
|
||
''' <summary>
|
||
''' 获取表达式转换后的结果是否为真,0、false、空字符为假,1,true为真, 其余为通过计算不为0判断真假
|
||
''' </summary>
|
||
''' <param name="exp"></param>
|
||
''' <returns></returns>
|
||
Public Shared Function GetBooleanResult(exp As String) As Boolean
|
||
If exp Is Nothing Then Return False
|
||
If String.IsNullOrWhiteSpace(exp) Then Return False
|
||
exp = exp.ToLower()
|
||
If exp.Equals($"false") Then Return False
|
||
If exp.Equals($"true") Then Return True
|
||
|
||
exp = exp.Replace("false", "0"c)
|
||
exp = exp.Replace("true", "1"c)
|
||
|
||
Dim str As String = GetDoubleExpressionResult(exp).ToString()
|
||
Console.WriteLine($"Result:{str}")
|
||
Return Math.Abs(GetDoubleExpressionResult(exp) - 0) > _doublePrecision
|
||
End Function
|
||
|
||
|
||
''' <summary>
|
||
''' 获取double类型运算式字符串后缀表达式
|
||
''' </summary>
|
||
''' <param name="str">字符串表达式</param>
|
||
''' <returns></returns>
|
||
Public Shared Function GetDoubleExpressionResult(str As String) As Double
|
||
Dim isNum As Boolean
|
||
Dim numStack As New Stack(Of Double)
|
||
Dim operatorStack As New Stack(Of Char)
|
||
Dim num1, num2 As Double
|
||
Dim priority As Integer
|
||
Dim ch As Char
|
||
str = UseMarkSymbolReplace(str)
|
||
For i As Integer = 0 To str.Length - 1
|
||
ch = str.Chars(i)
|
||
If ch >= "0"c AndAlso ch <= "9"c Then '数字处理
|
||
Dim tmpStr As String = String.Empty
|
||
Dim len As Integer = 1
|
||
tmpStr += ch
|
||
isNum = True
|
||
While isNum
|
||
If i + len >= str.Length Then Exit While
|
||
ch = str.Chars(i + len)
|
||
If ch >= "0"c AndAlso ch <= "9"c Then
|
||
tmpStr += ch
|
||
len += 1
|
||
ElseIf ch = "."c Then
|
||
tmpStr += ch
|
||
len += 1
|
||
Else
|
||
isNum = False
|
||
End If
|
||
End While
|
||
numStack.Push(Double.Parse(tmpStr))
|
||
i += len - 1
|
||
ElseIf CheckCharValidity(ch) = 1 Then '运算符号
|
||
If operatorStack.Count = 0 Then
|
||
operatorStack.Push(ch)
|
||
Else
|
||
priority = GetCharacterPriority(ch) '获取当前符号的优先级
|
||
While operatorStack.Count > 0 AndAlso GetCharacterPriority(operatorStack.Peek) < 11 AndAlso priority <= GetCharacterPriority(operatorStack.Peek)
|
||
num1 = numStack.Pop
|
||
num2 = numStack.Pop
|
||
numStack.Push(GetDoubleResult(num2, num1, operatorStack.Pop))
|
||
End While
|
||
If priority = 0 Then '为后半边括号
|
||
operatorStack.Pop()
|
||
Else '为前半边括号
|
||
operatorStack.Push(ch)
|
||
End If
|
||
End If
|
||
End If
|
||
|
||
If i = str.Length - 1 Then
|
||
priority = -1
|
||
While operatorStack.Count > 0 AndAlso GetCharacterPriority(operatorStack.Peek) < 11 AndAlso priority <= GetCharacterPriority(operatorStack.Peek)
|
||
num1 = numStack.Pop
|
||
num2 = numStack.Pop
|
||
numStack.Push(GetDoubleResult(num2, num1, operatorStack.Pop))
|
||
End While
|
||
End If
|
||
Next
|
||
|
||
Return Math.Round(numStack.Pop, 3) '计算保留小数位三位
|
||
End Function
|
||
|
||
''' <summary>
|
||
''' 获取小数类型运算结果
|
||
''' </summary>
|
||
''' <param name="num1">浮点型数1</param>
|
||
''' <param name="num2">浮点型数2</param>
|
||
''' <param name="c">运算符</param>
|
||
''' <returns></returns>
|
||
Public Shared Function GetDoubleResult(num1 As Double, num2 As Double, c As Char) As Double
|
||
Dim result As Double
|
||
Select Case c
|
||
Case "*"c
|
||
result = num1 * num2
|
||
Case "/"c
|
||
If Math.Abs(num2 - 0) < _doublePrecision Then Throw New Exception($"除数为0执行失败")
|
||
result = num1 / num2
|
||
Case "%"c
|
||
result = num1 Mod num2
|
||
Case "+"c
|
||
result = num1 + num2
|
||
Case "-"c
|
||
result = num1 - num2
|
||
Case "l"c '左移
|
||
result = CInt(num1) << CInt(num2)
|
||
Case "r"c '右移
|
||
result = CInt(num1) >> CInt(num2)
|
||
Case "&"c
|
||
result = CInt(num1) And CInt(num2)
|
||
Case "^"c
|
||
result = CInt(num1) Xor CInt(num2)
|
||
Case "|"c
|
||
result = CInt(num1) Or CInt(num2)
|
||
|
||
Case ">"c '大于等于
|
||
result = CInt(IIf(num1 > num2, 1, 0))
|
||
Case "<"c '大于等于
|
||
result = CInt(IIf(num1 < num2, 1, 0))
|
||
Case "="c '等于
|
||
result = CInt(IIf(Math.Abs(num1 - num2) < _doublePrecision, 1, 0))
|
||
Case "g"c '大于等于
|
||
result = CInt(IIf(num1 >= num2, 1, 0))
|
||
Case "s"c '小于等于
|
||
result = CInt(IIf(num1 <= num2, 1, 0))
|
||
Case "u"c '不等于
|
||
result = CInt(IIf(Math.Abs(num1 - num2) > _doublePrecision, 1, 0))
|
||
Case "e"c '等于
|
||
result = CInt(IIf(Math.Abs(num1 - num2) < _doublePrecision, 1, 0))
|
||
|
||
Case "a"c '逻辑与
|
||
If num1 > 0 AndAlso num2 > 0 Then
|
||
result = 1
|
||
Else
|
||
result = 0
|
||
End If
|
||
Case "o"c '逻辑或
|
||
If num1 > 0 OrElse num2 > 0 Then
|
||
result = 1
|
||
Else
|
||
result = 0
|
||
End If
|
||
Case Else
|
||
Throw New Exception($"未知的操作符:[{c}]")
|
||
End Select
|
||
Return result
|
||
End Function
|
||
|
||
|
||
''' <summary>
|
||
''' 获取符号的优先级
|
||
''' </summary>
|
||
''' <param name="c">运算符号</param>
|
||
''' <returns></returns>
|
||
Private Shared Function GetCharacterPriority(c As Char) As Integer
|
||
Dim intNum As Integer
|
||
Select Case c
|
||
Case "("c
|
||
intNum = 11
|
||
Case "*"c, "/"c, "%"c
|
||
intNum = 10
|
||
Case "+"c, "-"c
|
||
intNum = 9
|
||
Case "l"c, "r"c
|
||
intNum = 8
|
||
Case ">"c, "<"c, "b"c, "s"c
|
||
intNum = 7
|
||
Case "="c, "u"c, "e"c
|
||
intNum = 6
|
||
Case "&"c
|
||
intNum = 5
|
||
Case "^"c
|
||
intNum = 4
|
||
Case "|"c
|
||
intNum = 3
|
||
Case "a"c
|
||
intNum = 2
|
||
Case "o"c
|
||
intNum = 1
|
||
Case ")"c
|
||
intNum = 0
|
||
Case Else
|
||
Throw New Exception($"未知的操作符:[{c}]")
|
||
End Select
|
||
Return intNum
|
||
End Function
|
||
|
||
''' <summary>
|
||
''' 校验字符合法性,0为数字,1为运算符,-1为未使用的运算符
|
||
''' </summary>
|
||
''' <param name="c">需要校验的字符</param>
|
||
''' <returns></returns>
|
||
Private Shared Function CheckCharValidity(c As Char) As Integer
|
||
Select Case c
|
||
Case "1"c, "2"c, "3"c, "4"c, "5"c, "6"c, "7"c, "8"c, "9"c, "0"c
|
||
|
||
Return 0
|
||
Case "+"c, "-"c, "*"c, "/"c, "%"c, "^"c, "|"c, "&"c, "("c, ")"c, "o"c, "a"c, "l"c, "r"c, "."c,
|
||
"u"c, "g"c, "s"c, "<"c, ">"c, "="c, "e"c
|
||
|
||
Return 1
|
||
Case Else
|
||
Return -1
|
||
End Select
|
||
End Function
|
||
|
||
|
||
''' <summary>
|
||
''' 校验运算式字符串合法性(todo:待进一步完善检测逻辑)
|
||
''' </summary>
|
||
''' <param name="str">需要校验的字符串</param>
|
||
''' <returns></returns>
|
||
Public Shared Function CheckExpressionString(str As String) As Boolean
|
||
str = str.Replace(" ", "")
|
||
For i As Integer = 0 To str.Length - 1
|
||
If CheckCharValidity(str.Chars(i)) = -1 Then
|
||
Return False
|
||
End If
|
||
Next
|
||
Return True
|
||
End Function
|
||
|
||
|
||
|
||
|
||
'''' <summary>
|
||
'''' 获取int类型运算式字符串后缀表达式
|
||
'''' </summary>
|
||
'''' <param name="str">字符串表达式</param>
|
||
'''' <returns></returns>
|
||
'Public Shared Function GetIntegerExpressionResult(str As String) As Integer
|
||
' Dim isNum As Boolean
|
||
' Dim numberStack As New Stack(Of Integer)
|
||
' Dim operatorStack As New Stack(Of Char)
|
||
' Dim num1, num2, priority As Integer
|
||
' str = UseMarkSymbolReplace(str, False)
|
||
|
||
' For Each c As Char In str
|
||
' If c >= "0"c AndAlso c <= "9"c Then
|
||
' If isNum Then
|
||
' Dim tmp As Integer = numberStack.Pop
|
||
' tmp = tmp * 10 + Integer.Parse(c.ToString)
|
||
' numberStack.Push(tmp)
|
||
' Else
|
||
' numberStack.Push(Integer.Parse(c.ToString))
|
||
' isNum = True
|
||
' End If
|
||
' ElseIf CheckCharValidity(c) = 1 Then
|
||
' isNum = False
|
||
' If operatorStack.Count = 0 Then
|
||
' operatorStack.Push(c)
|
||
' Else
|
||
' priority = GetCharacterPriority(c)
|
||
' While operatorStack.Count > 0 AndAlso GetCharacterPriority(operatorStack.Peek) < 11 AndAlso priority <= GetCharacterPriority(operatorStack.Peek)
|
||
' num1 = numberStack.Pop
|
||
' num2 = numberStack.Pop
|
||
' numberStack.Push(GetIntegerResult(num2, num1, operatorStack.Pop))
|
||
' End While
|
||
' If priority = 0 Then
|
||
' operatorStack.Pop()
|
||
' Else
|
||
' operatorStack.Push(c)
|
||
' End If
|
||
' End If
|
||
' End If
|
||
|
||
' If c = str.Chars(str.Length - 1) Then
|
||
' priority = GetCharacterPriority(c)
|
||
' While operatorStack.Count > 0 AndAlso GetCharacterPriority(operatorStack.Peek) < 11 AndAlso priority <= GetCharacterPriority(operatorStack.Peek)
|
||
' num1 = numberStack.Pop
|
||
' num2 = numberStack.Pop
|
||
' numberStack.Push(GetIntegerResult(num2, num1, operatorStack.Pop))
|
||
' End While
|
||
' End If
|
||
' Next
|
||
|
||
' Return numberStack.Pop
|
||
'End Function
|
||
|
||
'''' <summary>
|
||
'''' 获取整形的运算结果
|
||
'''' </summary>
|
||
'''' <param name="num1">整数1</param>
|
||
'''' <param name="num2">整数2</param>
|
||
'''' <param name="c">运算符</param>
|
||
'''' <returns>预算结果</returns>
|
||
'Public Shared Function GetIntegerResult(num1 As Integer, num2 As Integer, c As Char) As Integer
|
||
' Dim result As Integer
|
||
' Select Case c
|
||
' Case "*"c
|
||
' result = num1 * num2
|
||
' Case "/"c
|
||
' result = num1 \ num2
|
||
' Case "%"c
|
||
' result = num1 Mod num2
|
||
' Case "+"c
|
||
' result = num1 + num2
|
||
' Case "-"c
|
||
' result = num1 - num2
|
||
' Case "L"c
|
||
' result = num1 << num2
|
||
' Case "R"c
|
||
' result = num1 >> num2
|
||
' Case "&"c
|
||
' result = num1 And num2
|
||
' Case "^"c
|
||
' result = num1 Xor num2
|
||
' Case "|"c
|
||
' result = num1 Or num2
|
||
|
||
' Case ">"c '大于等于
|
||
' result = CInt(IIf(num1 > num2, 1, 0))
|
||
' Case "<"c '大于等于
|
||
' result = CInt(IIf(num1 < num2, 1, 0))
|
||
' Case "="c '等于
|
||
' result = CInt(IIf(num1 = num2, 1, 0))
|
||
' Case "g"c '大于等于
|
||
' result = CInt(IIf(num1 >= num2, 1, 0))
|
||
' Case "l"c '小于等于
|
||
' result = CInt(IIf(num1 <= num2, 1, 0))
|
||
' Case "u"c '不等于
|
||
' result = CInt(IIf(num1 <> num2, 1, 0))
|
||
' Case "e"c '等于
|
||
' result = CInt(IIf(num1 = num2, 1, 0))
|
||
|
||
' Case "A"c
|
||
' If num1 > 0 AndAlso num2 > 0 Then
|
||
' result = 1
|
||
' Else
|
||
' result = 0
|
||
' End If
|
||
' Case "O"c
|
||
' If num1 > 0 OrElse num2 > 0 Then
|
||
' result = 1
|
||
' Else
|
||
' result = 0
|
||
' End If
|
||
' Case Else
|
||
' Throw New Exception($"未知的操作符:[{c}]")
|
||
' End Select
|
||
' Return result
|
||
'End Function
|
||
|
||
|
||
' Private Shared ReadOnly _operateList As New List(Of Char) From {"+"c, "*"c, "/"c, "%"c, "-"c, "^"c, "|"c, "&"c}
|
||
|
||
|
||
|
||
'Private Shared Function CheckDoubleResultChar(exp As String) As Boolean
|
||
' For i As Integer = 0 To exp.Length - 1
|
||
' If exp.Chars(i) > "0"c AndAlso exp.Chars(i) < "9"c Then
|
||
' '有效数字
|
||
' Continue For
|
||
' End If
|
||
|
||
|
||
' Select Case exp.Chars(i)
|
||
' Case "-"c
|
||
' '减运算符前不能为运算符
|
||
|
||
|
||
' Case "+"c, "*"c, "/"c, "%"c
|
||
' '运算符前后位必须为数字
|
||
' If i = 0 Then Return False '不能为开头
|
||
' If i = exp.Length - 1 Then Return False '不能为结尾
|
||
' ' If exp.Chars(i + 1) Then Return False'下一位不能
|
||
|
||
' Case "!"c'是否应该支持
|
||
' '按位操作前后须有数字
|
||
|
||
' Case "^"c
|
||
' '按位操作前后须有数字
|
||
|
||
' Case "|"c
|
||
' '按位操作前后须有数字
|
||
|
||
' '逻辑操作前后须有数字
|
||
|
||
|
||
' '确定是否为逻辑操作
|
||
|
||
' Case "&"c
|
||
' '按位操作前后须有数字
|
||
|
||
' '逻辑操作前后须有数字
|
||
|
||
|
||
' '确定是否为逻辑操作
|
||
|
||
' Case ">"c
|
||
' '判断逻辑符或是组合符号
|
||
|
||
' Case "<"c
|
||
' '判断逻辑符或是组合符号
|
||
|
||
' Case "="c
|
||
|
||
' '判断逻辑符或是组合符号
|
||
|
||
' Case "."c
|
||
' '前后必为数字
|
||
|
||
' Case "("c
|
||
' '括号必须匹配
|
||
|
||
' Case ")"c
|
||
' '括号必须匹配
|
||
|
||
' Case " "c
|
||
' '空格忽略
|
||
|
||
' Case Else
|
||
|
||
|
||
' End Select
|
||
' Next
|
||
|
||
|
||
' Return False
|
||
'End Function
|
||
|
||
|
||
''' <summary>
|
||
''' 返回表达式的值 'Momo 2022-11-10 增加 、Momo 2023-12-15 错误时抛出错误而不是弹窗
|
||
''' </summary>
|
||
''' <returns></returns>
|
||
Public Shared Function ExecuteLimit(ByVal strExpression As String, ByRef dblResult As String) As Boolean
|
||
'检测有无需要计算的表达式
|
||
If String.IsNullOrWhiteSpace(strExpression) Then
|
||
'’MsgBox(strExpression & " 是空白值或表达式,执行失败!")
|
||
'Throw New Exception(strExpression & " 是空白值或表达式,执行失败!") 'Momo 2023-12-15 错误时抛出错误而不是弹窗
|
||
Return False
|
||
End If
|
||
|
||
'检测表达式的有效性
|
||
If StringExpression.CheckExpressionString(strExpression) = False Then
|
||
'’MsgBox(strExpression & " 是无效值或表达式,执行失败!")
|
||
'Throw New Exception(strExpression & " 是无效值或表达式,执行失败!") 'Momo 2023-12-15 错误时抛出错误而不是弹窗
|
||
Return False
|
||
End If
|
||
|
||
Try
|
||
dblResult = StringExpression.GetDoubleExpressionResult(strExpression).ToString()
|
||
Return True
|
||
Catch ex As Exception
|
||
'’MsgBox(strExpression & " 表达式计算失败,执行失败!")
|
||
'Throw New Exception(strExpression & " 表达式计算失败,执行失败!") 'Momo 2023-12-15 错误时抛出错误而不是弹窗
|
||
Return False
|
||
End Try
|
||
|
||
End Function
|
||
|
||
|
||
End Class
|
||
End Namespace |