【问题标题】:Text templating engine/function in Visual Basic for ApplicationsVisual Basic for Applications 中的文本模板引擎/函数
【发布时间】:2014-06-25 07:37:12
【问题描述】:

在我的公司,VBA 的一个常见用法是根据 Excel 表格中输入的信息生成源代码。鉴于 VBA 的本机字符串操作,执行此操作的代码编写起来乏味且可读性差。

一个简单的例子(这些变得更复杂)是:

Print #fileIdentifier, SSpace(9) & "update_" & print_string & "[" & CStr(j) & "] <= 1'b0;"
Print #fileIdentifier, SSpace(9) & print_string & "_ack_meta" & "[" & CStr(j) & "] <= 1'b0;"
Print #fileIdentifier, SSpace(9) & print_string & "_ack_sync" & "[" & CStr(j) & "] <= 1'b0;"

我正在寻找 VBA 中的解决方案,它允许我使用“文本模板”来指定它,因此定义一个看起来像这样的文本

    update_@name@[@bit@] <= 1'b0;
    @name@_ack_meta[@bit@] <= 1'b0;
    @name@_ack_sync[@bit@] <= 1'b0;

并有一个函数/方法调用,传递@name@ 和@bit@ 的值,将@name@ 和@bit@ 的所有实例替换为相应的值。

【问题讨论】:

  • 将模板文本存储在文件中,加载并调用 replace$()?
  • 你为什么语言生成代码?
  • 感谢@AlexK。的建议。有数百个这样的代码被填写,所以文本必须内联存储。我认为手动调用来替换 @label@ 字符串的所有实例都可以,但它仍然比我要查找的代码多得多,尤其是当模板文本变得更长并且包含更多要替换的“变量”时。跨度>
  • 好吧,您总是需要更改打印语句以适应任何新内容,您的示例可以重新分解为一个采用带有序号插入标记的模板字符串的单个过程:update_@1@[@2@]\r\nXXX@3@ 然后是一组值变量
  • 将“模板”存储在文本文件中。在 VBA 中打开它并根据存储在 Excel 工作表中的数据执行替换。将其写入一个新的唯一文件。

标签: vba excel


【解决方案1】:

基本插入函数:

Function insert(template As String, ParamArray inserts() As Variant) As String
    Dim i As Long
    For i = 0 To UBound(inserts)
        template = Replace$(template, "%" & i + 1 & "%", inserts(i))
    Next

    '// some special cases perhaps
    template = Replace$(template, "%SSPACE%", SSpace(9))
    template = Replace$(template, "\r\n", VbCrLf)

    insert = template
End Function

对于

?insert("Foo %1% Bar %2% Qux %3% (%1%)", "A", "B", "C")

Foo A Bar B Qux C (A)

地图(添加对Microsoft Scripting Runtime的引用):

Dim col As New Scripting.Dictionary
col("name") = "bob"
col("age") = 35

MsgBox insert2("Hello %name% you are %age%", col)

...

Function insert2(template As String, map As Scripting.Dictionary) As String
    Dim name
    For Each name In map.Keys()
        template = Replace$(template, "%" & name & "%", map(name))
    Next
    insert2 = template
End Function

【讨论】:

  • 谢谢。这行得通。在这变得足够干净以达到我最终想要的效果之前,还有一些调整,但这绝对解决了我的问题。
【解决方案2】:

Alex K.,感谢您的解决方案。

这是我扩展它的方法(如果有更好的方法,请随时告诉我)

Function FillTemplateGeneric(template As Variant, map As Scripting.Dictionary) As String
    Dim name
    Dim out_text As String

    ' Handle multiple ways of receiving the template string
    If VarType(template) = vbString Then
        out_text = template
    ElseIf VarType(template) = vbArray Then
        out_text = Join(template, vbCrLf)
    ElseIf TypeName(template) = "String()" Then
        out_text = Join(template, vbCrLf)
    ElseIf TypeName(template) = "Variant()" And TypeName(template(LBound(template, 1))) = "String" Then
        out_text = Join(template, vbCrLf)
    Else
        MsgBox "Unknown Var Type passed to FillTemplateGeneric as first argument:" & vbCrLf & TypeName(template)
        Err.Raise vbObjectError + 513, "FillTemplateGeneric", "Unknown Var Type passed to FillTemplateGeneric as first argument:" & vbCrLf & TypeName(template)
    End If

    For Each name In map.Keys()
        out_text = Replace$(out_text, "%" & name & "%", map(name))
    Next
    FillTemplateGeneric = out_text
End Function

这允许它接受多种格式的调用:

' Common dictionary for expansion
Dim col As New Scripting.Dictionary
col("name") = print_string

' Using inline text for template
MsgBox FillTemplateGeneric("test text with %name% name - just string", col)

' Using a multi-line string
Dim template As String
templ_text = "         update_%name% <= 1'b0; // 1 - manual multi-line string" & _
    vbCrLf & "         %name%_ack_meta <= 1'b0; // " & _
    vbCrLf & "         %name%_ack_sync <= 1'b0; // "
MsgBox FillTemplateGeneric(templ_text, col)

' Using an array of strings
Dim ttext(1 To 3) As String
ttext(1) = "         update_%name% <= 1'b0; // 2 - manual array of strings"
ttext(2) = "         %name%_ack_meta <= 1'b0; // "
ttext(3) = "         %name%_ack_sync <= 1'b0; // "
MsgBox FillTemplateGeneric(ttext, col)

' Using an inline array of strings 
MsgBox FillTemplateGeneric(Array( _
    "         update_%name% <= 1'b0; // 3 - immediate array of strings", _
    "         %name%_ack_meta <= 1'b0; // ", _
    "         %name%_ack_sync <= 1'b0; // " _
    ), col)

【讨论】:

  • 不错。您可以通过将逻辑填充到一个类中并进行一系列 template.add "Line 1" ... template.add "line x" 调用来整理多行 concats/ &_。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-03-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多