|
楼主 |
发表于 2016-5-13 18:14
|
显示全部楼层
本帖最后由 cbtaja 于 2016-5-21 08:56 编辑
再次更新求值函数:
- Function XEVAL(ByVal exp As String) '作者cbtaja 2016-5-13
- If Len(exp) = 0 Then XEVAL = "": Exit Function
- exp = ExpClean(exp)
- If Len(exp) = 0 Then XEVAL = "": Exit Function
- If Len(Replace(exp, "(", "")) - Len(Replace(exp, ")", "")) Then XEVAL = "括号不成对": Exit Function
- XEVAL = mEval(exp) '对Exp分步计算,可以应对超长的Exp计算式
- ' XEVAL = Evaluate(exp)'如果exp的长度总是不超过255,则可以用Evaluate简化计算过程。
- ' If IsError(XEVAL) Then XEVAL = EvalErrInfo(CStr(XEVAL)) & Exp
- End Function
- Function EvalErrInfo(ByVal exp As String)
- Select Case CStr(exp)
- Case "Error 2000": EvalErrInfo = "缺少参数:"
- Case "Error 2007": EvalErrInfo = "除数为○:"
- Case "Error 2015": EvalErrInfo = "参数类型不符:"
- Case "Error 2023": EvalErrInfo = "单元格引用无效:"
- Case "Error 2029": EvalErrInfo = "函数名无效:"
- Case "Error 2036": EvalErrInfo = "参数超出范围:"
- Case "Error 2042": EvalErrInfo = "参数无效:"
- Case Else:
- End Select
- End Function
- Function ExpClean$(ByVal exp As String)
- Set reg = CreateObject("VBScript.RegExp")
- reg.Global = True
- reg.Pattern = "\[[^\]\[]*\]" '忽略计算式中的[注释内容]
- exp = reg.Replace(exp, "")
- a = [{"+","+";"-","-";"×","*";"÷","/";"(","(";")",")";"%","*0.01"}] '“兼容”中文数学运算符'
- For i = 1 To UBound(a)
- reg.Pattern = a(i, 1)
- exp = reg.Replace(exp, a(i, 2))
- Next
- If InStr(exp, "[") Or InStr(exp, "]") Then ExpClean = "[]不成对": Exit Function
- reg.Pattern = "[^\x20-\x7e]+" '仅保留“基本拉丁字符”
- ExpClean = reg.Replace(exp, "")
- End Function
- Function sEval(ByVal exp As String)
- exp = UCase(exp)
- Set reg = CreateObject("VBScript.RegExp")
- reg.Global = False
- sPttnNumber = "\d+(\.\d+){0,1}(E[+-]\d+(\.\d+){0,1}){0,1}" '数值(含整数、小数、科学计数法表示的数值)
- sPttnCalChar = [{"\^","[\*\/]","[\+\-]"}] '依次进行乘幂、乘除、加减运算
- For i = 1 To UBound(sPttnCalChar)
- reg.Pattern = "(?:\b)" & sPttnNumber & sPttnCalChar(i) & sPttnNumber
- Do While reg.test(exp)
- Set matches = reg.Execute(exp)
- tmp = matches(0)
- sEval = Evaluate(tmp)
- If IsError(sEval) Then sEval = EvalErrInfo(CStr(sEval)) & tmp: Exit Function
- exp = reg.Replace(exp, sEval)
- If Len(exp) <= 255 Then Exit Do
- Loop
- Next
-
- 'Evaluate("1") 无法计算,所以按条件处理为两次求反
- If Len(exp) = 1 Then sEval = Evaluate("--" & exp) Else sEval = Evaluate(exp)
-
- If IsError(sEval) Then sEval = EvalErrInfo(CStr(sEval)) & exp
- End Function
- Function mEval(ByVal exp As String)
- Set reg = CreateObject("VBScript.RegExp")
- reg.Global = True
- reg.Pattern = "\w+(\([^\(\)]*\))"
- Do While reg.test(exp)
- Set matches = reg.Execute(exp)
- For i = matches.Count To 1 Step -1
- mEval = sEval(matches(i - 1).Value)
- If Not IsNumeric(mEval) Then Exit Function
- exp = reg.Replace(exp, mEval)
- Next
- Loop
- mEval = sEval(exp)
- End Function
- Function XEval(ByVal Exp As String) '作者cbtaja 2016-5-13
- '自动转换中文运算符+-×÷
- '允许百分号%和科学计数符号e
- '允许数学函数(要以前缀m.或Math.作标记,注意名称和参数类型要正确)
- '允许中文直接注释,
- '允许用成对中括号(即[])包含任意注释字符,
- '剩余不成对的中括号"["、"]"将被忽略
- '最后,如果算式仍无法计算,则报告出错的变量
- '------------------------------------------------
- Dim oJs As Object, XX As String, errinfo, a, i&
- Set oJs = CreateObject("ScriptControl")
- oJs.Language = "JScript"
- Exp = oJs.eval("'" & Exp & "'.replace(/\[[^\]\[]*\]/g,"""")") '清除成对中括号[]及其中的内容
- If IsEmpty(Exp) Then Exit Function
-
- a = [{"+","+";"-","-";"×","*";"÷","/";"[","";"]","";"%","*0.01"}] '替换不规范的运算符
- For i = 1 To UBound(a)
- Exp = Replace(Exp, a(i, 1), a(i, 2))
- Next
- If IsEmpty(Exp) Then Exit Function
-
- Exp = oJs.eval("'" & Exp & "'.replace(/[^\x21-\x7e]/g,"""")") '仅保留ASC码为33至125之间的字符
- If IsEmpty(Exp) Then Exit Function
-
- Exp = oJs.eval("'" & Exp & "'.replace(/\bm\./ig,""Math."")") '把“m.”(或“M.”)替换为“Math.”,以支持Js的数学函数
- If IsEmpty(Exp) Then Exit Function
-
- On Error GoTo errhdl
- XEval = oJs.eval(Exp) '对经过整理后的计算式进行求值
- If IsEmpty(XEval) Then GoTo errhdl
- XX = CStr(XEval)
-
- For Each errinfo In Array("R", "IND", "INF", "QNAN", "SNAN")
- If InStr(XX, ".#" & errinfo) Then GoTo errhdl
- Next
- Exit Function
- errhdl:
- XEval = "请修正:" & Trim(oJs.eval("'" & Exp & "'.replace(/[^\w.\/]+/ig,"""")"))
- End Function
复制代码
用JSCript的EVAL方法,也可以做很多事情:
- Function ev(ByVal s As String)
- Set oJs = CreateObject("ScriptControl")
- oJs.Language = "JScript"
- ev = oJs.eval(s)
- End Function
- Private Sub cs()
- Dim sss(0 To 9)
- sss(0) = "(2,Math.PI/2)"
- sss(1) = "小黄的数学考试得了95分,评价为A;语文得了92分,评价为B+,他的父亲奖励了他一双价值888.88元的轮滑鞋。"
- sss(2) = "2+Math.sin(Math.PI/4)"
- aaa = ev(sss(0)) '数学函数式求值
- MsgBox aaa
- aaa = ev("'" & sss(1) & "'.replace(/[\s\u3400-\ufe4f]+/ig,"" "")") '文本正则替换
- MsgBox aaa
- aaa = ev(sss(2)) '数学函数式求值
- MsgBox aaa
- aaa = ev("'" & sss(1) & "'.length") '求字符串长度
- MsgBox aaa
- aaa = ev("'" & sss(1) & "'.substr(3,9)") '取子符串(类似于VB的mid函数)
- MsgBox aaa
- aaa = ev("Math.e") '
- MsgBox aaa
- aaa = ev("Math.E") '自然对数的底E。要注意:Script中的变量名是区分大小写的。
- MsgBox aaa
- '通过构造语句,以JSCript的EVAL方法解释执行语句,可以完成很多事情。(只要熟知JSCript的内置对象的属性、方法、函数就好办)
- Stop
- End Sub
复制代码
- Function RegReplace(ByVal s As String, ByVal patn As String, Optional ByVal delemit As String = "")
- '中文:"[\u3400-\ufe4f]"
- '数字:"[\d]"或"[0-9]"
- '数值:"\d+(\.\d+|)(e[+-]\d+)?%?"
- '字母:"[a-zA-Z]"
- Set reg = CreateObject("vbscript.Regexp")
- reg.Global = True
- reg.Pattern = patn
- RegReplace = reg.Replace(s, delemit)
- End Function
复制代码
补充内容 (2016-7-4 21:47):
求值函数更新,修正1处Bug,请从23楼下载附件中查看。 |
|