VBA 字符串插值语法

VBA string interpolation syntax

什么是 VBA 字符串插值语法?存在吗?

我想使用 Excel VBA 来格式化一个字符串。 我有一个变量 foo,我想将它放入一个范围的字符串中。

Dim row as Long
row = 1

myString = "$row:$row"

我希望将字符串中的 $row 插入为“1”

我相信这很好用。

Dim row as Long
Dim s as String

row = 1
s = "$" & row & ":$" & row

除非您想要类似于 Python 或 C# 的 {} 表示法,否则这是执行此操作的标准方法。

您还可以构建自定义 Format 函数。

Public Function Format(ParamArray arr() As Variant) As String

    Dim i As Long
    Dim temp As String

    temp = CStr(arr(0))
    For i = 1 To UBound(arr)
        temp = Replace(temp, "{" & i - 1 & "}", CStr(arr(i)))
    Next

    Format = temp
End Function

用法与C#类似,只是不能在字符串中直接引用变量。例如。 Format("This will {not} work")Format("This {0} work", "will").

Public Sub Test()

    Dim s As String

    s = "Hello"
    Debug.Print Format("{0}, {1}!", s, "World")
End Sub

打印出 Hello, World! 到即时 Window。

使用Key\Value对

另一种模拟字符串插值的替代方法是将 key\value 对作为 ParamArray 传递并相应地替换键。

需要注意的是,如果元素数量不是偶数,则会引发错误。

' Returns a string that replaced special keys with its associated pair value.
Public Function Inject(ByVal source As String, ParamArray keyValuePairs() As Variant) As String

    If (UBound(keyValuePairs) - LBound(keyValuePairs) + 1) Mod 2 <> 0 Then
        Err.Raise 5, "Inject", "Invalid parameters: expecting key/value pairs, but received an odd number of arguments."
    End If

    Inject = source

    ' Replace {key} with the pairing value.
    Dim index As Long
    For index = LBound(keyValuePairs) To UBound(keyValuePairs) Step 2
        Inject = Replace(Inject, "{" & keyValuePairs(index) & "}", keyValuePairs(index + 1), , , vbTextCompare)
    Next index

End Function

简单示例

这是一个简单的示例,说明如何实现它。

Private Sub testingInject()
    Const name As String = "Robert"
    Const age As String = 31
    Debug.Print Inject("Hello, {name}! You are {age} years old!", "name", name, "age", age)
    '~> Hello, Robert! You are 31 years old!
End Sub

虽然这可能会增加一些额外的字符串,但在我看来,这使得阅读长字符串变得更加容易。

查看使用串联的相同简单示例:

Debug.Print "Hello, " & name & "! You are " & age & " years old!"

使用Scripting.Dicitionary

真的,Scripting.Dictionary 非常适合,因为它们只是 key/value 对。这将是对我上面的代码的一个简单调整,只需将 Dictionary 作为参数并确保键匹配。

Public Function Inject(ByVal source As String, ByVal data As Scripting.Dictionary) As String
    Inject = source

    Dim key As Variant
    For Each key In data.Keys
        Inject = Replace(Inject, "{" & key & "}", data(key))
    Next key
End Function

词典示例

以及将其用于词典的示例:

Private Sub testingInject()
    Dim person As New Scripting.Dictionary
    person("name") = "Robert"
    person("age") = 31
    Debug.Print Inject("Hello, {name}! You are {age} years old!", person)
    '~> Hello, Robert! You are 31 years old!
End Sub

其他注意事项

集合听起来也不错,但无法访问密钥。那样可能会变得更乱。

如果使用字典方法,您可以创建一个简单的工厂函数来轻松创建字典。你可以在我的 Github Library Page.

上找到一个例子

为了模仿函数重载以提供所有不同的方式,您可以创建主 Inject 函数和 运行 在其中创建 select 语句。

如果需要,这是执行此操作所需的所有代码:

Public Function Inject(ByVal source As String, ParamArray data() As Variant) As String

    Dim firstElement As Variant
    assign firstElement, data(LBound(data))

    Inject = InjectCharacters(source)

    Select Case True
        Case TypeName(firstElement) = "Dictionary"
            Inject = InjectDictionary(Inject, firstElement)

        Case InStr(source, "{0}") > 0
            Inject = injectIndexes(Inject, CVar(data))

        Case (UBound(data) - LBound(data) + 1) Mod 2 = 0
            Inject = InjectKeyValuePairs(Inject, CVar(data))

        Case Else
            Err.Raise 5, "Inject", "Invalid parameters: expecting key/value pairs or Dictionary or an {0} element."
    End Select

End Function

Private Function injectIndexes(ByVal source As String, ByVal data As Variant)
    injectIndexes = source

    Dim index As Long
    For index = LBound(data) To UBound(data)
        injectIndexes = Replace(injectIndexes, "{" & index & "}", data(index))
    Next index
End Function


Private Function InjectKeyValuePairs(ByVal source As String, ByVal keyValuePairs As Variant)
    InjectKeyValuePairs = source

    Dim index As Long
    For index = LBound(keyValuePairs) To UBound(keyValuePairs) Step 2
        InjectKeyValuePairs = Replace(InjectKeyValuePairs, "{" & keyValuePairs(index) & "}", keyValuePairs(index + 1))
    Next index
End Function

Private Function InjectDictionary(ByVal source As String, ByVal data As Scripting.Dictionary) As String
    InjectDictionary = source

    Dim key As Variant
    For Each key In data.Keys
        InjectDictionary = Replace(InjectDictionary, "{" & key & "}", data(key))
    Next key
End Function

' QUICK TOOL TO EITHER SET OR LET DEPENDING ON IF ELEMENT IS AN OBJECT
Private Function assign(ByRef variable As Variant, ByVal value As Variant)
    If IsObject(value) Then
        Set variable = value
    Else
        Let variable = value
    End If
End Function

End Function

Private Function InjectCharacters(ByVal source As String) As String

    InjectCharacters = source

    Dim keyValuePairs As Variant
    keyValuePairs = Array("n", vbNewLine, "t", vbTab, "r", vbCr, "f", vbLf)

    If (UBound(keyValuePairs) - LBound(keyValuePairs) + 1) Mod 2 <> 0 Then
        Err.Raise 5, "Inject", "Invalid variable: expecting key/value pairs, but received an odd number of arguments."
    End If

    Dim RegEx As Object
    Set RegEx = CreateObject("VBScript.RegExp")
    RegEx.Global = True

    ' Replace is ran twice since it is possible for back to back patterns.
    Dim index As Long
    For index = LBound(keyValuePairs) To UBound(keyValuePairs) Step 2
        RegEx.Pattern = "((?:^|[^\])(?:\{2})*)(?:\" & keyValuePairs(index) & ")+"
        InjectCharacters = RegEx.Replace(InjectCharacters, "" & keyValuePairs(index + 1))
        InjectCharacters = RegEx.Replace(InjectCharacters, "" & keyValuePairs(index + 1))
    Next index

End Function

我有一个库函数 SPrintF() 可以满足您的需要。

它使用 VBA 的 ParamArray() 功能,用数量可扩展的参数替换提供的字符串中出现的 %s。

用法:
SPrintF("%s:%s", 1, 1) => "1:1"
SPrintF("Property %s added at %s on %s", "88 High St, Clapham", Time, Date) => ""Property 88 High St, Clapham added at 11:30:27 on 25/07/2019"

Function SprintF(strInput As String, ParamArray varSubstitutions() As Variant) As String

    'Formatted string print: replaces all occurrences of %s in input with substitutions

    Dim i As Long
    Dim s As String

    s = strInput

    For i = 0 To UBound(varSubstitutions)
        s = Replace(s, "%s", varSubstitutions(i), , 1)
    Next

    SprintF = s 

End Function

只是作为脚注添加,此想法的灵感来自 C language printf function

我使用与 类似的代码,除了我使用正则表达式替换出现的事件并允许用户为占位符指定 描述 。当你有一个很大的 table(比如在 Access 中)有很多字符串或翻译时,这很有用,这样你仍然知道每个数字的含义。

Function Format(ByVal Source As String, ParamArray Replacements() As Variant) As String
    
    Dim Replacement As Variant
    Dim i As Long
    For i = 0 To UBound(Replacements)
        
        Dim rx As New RegExp
        With rx
            .Pattern = "{" & i & "(?::(.+?))?}"
            .IgnoreCase = True
            .Global = True
        End With
                
        Select Case VarType(Replacements(i))
            Case vbObject
                If Replacements(i) Is Nothing Then
                    Dim Matches As MatchCollection
                    Set Matches = rx.Execute(Source)
                    If Matches.Count = 1 Then
                        Dim Items As SubMatches: Set Items = Matches(0).SubMatches
                        Dim Default As String: Default = Items(0)
                        Source = rx.Replace(Source, Default)
                    End If
                End If
            Case vbString
                Source = rx.Replace(Source, CStr(Replacements(i)))
        End Select
        
    Next

    Format = Source

End Function

Sub TestFormat()

    Debug.Print Format("{0:Hi}, {1:space}!", Nothing, "World")
    
End Sub