【问题标题】:Evaluate a string boolean expression with VBA使用 VBA 评估字符串布尔表达式
【发布时间】:2021-04-09 09:53:33
【问题描述】:

我需要在 VBA 中计算一个字符串布尔表达式,例如:“1 and (0 or 0 or 1)” (这应该评估为“1”。)

我正在寻找类似 Python“eval”函数的东西。有没有一种简单的方法可以用 VBA 做到这一点?谢谢!

【问题讨论】:

  • eval 的存在取决于宿主环境。例如。 Excel has it,例如Word 没有。
  • 您能详细说明一下吗?当我尝试在 excel 中使用 Eval 时,我收到一个编译错误,提示“未定义子或函数”。
  • 因为Excel的eval叫做Evaluate。请看链接。
  • 谢谢,我也知道这个功能,但是在尝试了几种不同的输入表达式的方式后无法让它工作。我能否麻烦您提供一个有效的 VBA 命令来评估字符串布尔表达式?谢谢!
  • 您不能使用 Evaluate 运行随机 VBA 语法 - 您需要使用兼容的东西(即使用工作表函数并以相同的语法表示):即 Evaluate("1 and 0") 将产生一个错误,而Evaluate("AND(1,0)") 将给出True。在 python/js "eval" 的 VBA 中没有真正的等价物:除非您计算使用 VBE 方法将要评估的 VBA 代码写入方法并运行它(请参阅cpearson.com/excel/vbe.aspx

标签: vba boolean eval


【解决方案1】:

除了 omegastripes 的回答,请注意脚本控制是 32 位的。随着 64 位成为常态,我不愿意使用任何仅 32 位的解决方案。

有关 64 位脚本控制替代方案,请参阅:

Microsoft Script Control 64 bit?

但这仍然是一个尴尬的解决方案,我不会推荐它。它需要用户安装一个新的 DLL,并且需要 VBA 程序员调用 JavaScript,尽管 JS 确实有一个相对安全的“eval”函数。从安全的角度来看,这可能不是最好的方法,而且由于 Microsoft 没有 64 位版本,因此 32 位版本已被弃用,或者至少被弃用。

以下是我更喜欢的其他一些方法:

  1. 变得更有趣并使用正则表达式:

How to use Regular Expressions (Regex) in Microsoft Excel both in-cell and loops

但是,RegEx 处理字符串比处理数字要好得多。 (如果您想将它与数字一起使用,那么您可以尝试类似于我在下面演示的方法,即单独进行数字评估。)

  1. 使用处理布尔表达式的 VBA 脚本,例如 this one 用于搜索来自 Ashley Harris 的字符串。 Harris 的函数适用于文本或纯布尔值(带有 True 和 False 的表达式)。如果您需要评估数字,那么您将不得不尝试其他方法,或者添加一个图层。

对于添加层,我最简单的建议是将每个短数字表达式括在括号中,并在传递之前对其进行评估。下面,我展示了一个在这种括号内计算表达式的简单示例(在本例中为“[[”和“]]”),因此用户可以将他的自定义表达式写成类似“[[(20 > (15+1 ) )]] AND ([[7>2]] OR [[5=4]])"。

所以,=BacketEval("[[(20 > (15+1) )]] AND ([[7>2]] OR [[5=4]])") 只会将其简化为“(True) AND (True OR False)”,您可以将其插入布尔解析器。类似的方法也适用于其他解析器。

Function BracketEval(BoolExp As String, Optional OpenBracket = "[[", Optional CloseBracket = "]]") As String

    Dim BoolStartPos As Integer
    Dim BoolEndPos As Integer
    Dim BoolToEval As String
    Dim BoolEvaled As String

    BoolStartPos = InStr(BoolExp, OpenBracket)
    BracketEval = BoolExp

    Do While BoolStartPos <> 0

        BoolEndPos = InStr(BoolStartPos + 1, BracketEval, CloseBracket)
        BoolToEval = Mid(BracketEval, BoolStartPos + Len(OpenBracket), BoolEndPos - BoolStartPos - Len(CloseBracket))
        BoolEvaled = CStr(Application.Evaluate(BoolToEval))

        BracketEval = Left(BracketEval, BoolStartPos - 1) _
         & BoolEvaled _
         & Right(BracketEval, Len(BracketEval) - BoolEndPos - Len(CloseBracket) + 1)

        BoolStartPos = InStr(BoolStartPos + Len(OpenBracket) - 1, BracketEval, OpenBracket)

    Loop

End Function

一个完整的解析器,甚至是一个可以将括号添加到 VBA 样式表达式的解析器,超出了我想要使用已经很长(虽然不是很感激!)答案的地方,而且这变得太复杂了,虽然我已经涉足这些。

Harris 的布尔解析器是我所见过的最好的 VBA 解析器,而且非常聪明,但请注意,如果它不喜欢输入,它可能会冻结。在the page,我为他的软件添加了一些关于如何避免这种情况的 cmets:基本上,添加一个计数器,如果它运行得太高,则退出。

接下来,这里介绍了一些不寻常的布尔解析解决方案:

Evaluate Excel VBA boolean condition (not a formula)

其中最好的是:

  1. 如果安装了 Access,则通过 Access 对象评估 VBA 中的表达式。如果您只需要评估几个表达式并知道用户将拥有访问权限,那么这是一个非常简单的解决方案。

你也可以:

  1. Create an Internet Explorer browser object 并通过 JavaScript 评估布尔值。同样,也许不是超级优雅,但这里的优点是大多数 Windows 计算机都安装了 IE。 (如果您碰巧遇到自动化问题,请尝试创建 InternetExplorerMedium 对象,而不是普通的旧 InternetExplorer。)

对于某些人来说,下面可能是最快和最优雅的答案,特别是如果你想使用更快的引擎来处理一堆这样的问题:

  1. 您可以从另一个脚本引擎调用外部脚本,例如 Python、AutoHotkey、R、PHP 或类似的东西。 PowerShell 可能有意义,但调整其安全设置可能对某些用户来说是一件麻烦事。最快的(计算上)可能是调用外部脚本,然后从该脚本中直接访问 Excel 电子表格对象(以获取表达式),进行所有必要的评估,然后关闭脚本。

从那里开始,可用选择的质量逐渐减少。

  1. 同样如上面链接中所引用的,如果您愿意降低 Excel 的安全设置,您可以通过暴力 VBA 代码注入来评估表达式。这几乎可以肯定是一个糟糕的主意,但如果你只使用一个电子表格,从不使用互联网,不与任何人共享代码,和/或天生叛徒,不像莱因霍尔德法官那样拒绝“按照书本”去做那么它可能是最快的。如果您使用这种方法(您是坏妈妈),我什至可能准备禁用您的防病毒软件中的启发式扫描! (如果这不能让你相信这是一个坏主意,我想什么都不会。)

如果您只有几个表达式要计算,并且它们是静态的,那么您可以将它们转换为标准的 Excel 电子表格语言。但是,如果您想说接受用户的表达式,那么要求他们使用 Excel 在其常规函数式语言中使用的笨拙的括号 AND、OR 和 NOT 是非常笨拙的。 VBA 自己对表达式的求值看起来更正常,但是 VBA 不能轻松地将代码作为字符串传递给自己(这通常是一件好事)。

【讨论】:

  • 当我们的想法被抹去时,我们应该如何提出关于如何改进这个网站的问题?
  • 不要在这里添加任何噪音,但我努力为这个问题创造出我在任何地方看到的最彻底的答案。一个半月后得分为-1。我在这里看到了有缺陷的答案,得分为 100 - 但我缺乏评论的代表。也许这不是最受欢迎的话题,但是哎哟!消极的。
  • 看我的回答,你可能会感兴趣:)
【解决方案2】:

您可以调用ScriptControl ActiveX 的.Eval() 方法来避免任何字符串预处理:

Sub TestEvaluateExpression()
    Dim varResult As Variant
    Dim strEvalContent As String
    strEvalContent = "1 and (0 or 0 or 1)"
    varResult = Eval(strEvalContent)
    MsgBox varResult
End Sub

Function Eval(strEvalContent As String) As Variant
    With CreateObject("ScriptControl")
        .Language = "VBScript"
        Eval = .Eval(strEvalContent)
    End With
End Function

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-05-28
    • 1970-01-01
    • 1970-01-01
    • 2013-12-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多