【问题标题】:Replace multiple values in Outlook mail htmlbody替换 Outlook 邮件 htmlbody 中的多个值
【发布时间】:2020-03-01 20:39:26
【问题描述】:

我正在使用一个包含各种占位符的 .oft(Outlook 模板)文件。我想用字符串替换占位符

oft 文件中的占位符是:

亲爱的~emp~ ~哇~

我正在使用下面的代码进行替换:

.htmlbody = Vtemplatebody
'replacing Dear with Dear Name, name in Column A
strFind = "Dear ~emp~"
strNew = "Dear " & ws.Cells(i, "f")
.htmlbody = Replace(.htmlbody, strFind, strNew)

'Replace award
strFind1 = "~awd~"
strNew1 = ws.Cells(i, "h")
.htmlbody = Replace(.htmlbody, strFind1, strNew1)

第一个替换即 ~emp~ 发生正确,即字符串被替换为 ws.Cells(i, "f") 中的值。但是第二个即 ~awd~ 并没有被 strNew1 中的值替换。

我正在做的事情有问题还是要以其他方式完成?

【问题讨论】:

  • 也许 strNew1 没有作为字符串出现?强制使用"" & ws.Cells(i, "h")

标签: vba outlook


【解决方案1】:

你运行过这段代码吗?如果是在哪里?我希望编译器将.htmlbody 转换为.HtmlBody

您给我们的代码太少无法确定,但我怀疑通配符。对于 Replace 方法,星号 (*)、问号 (?) 和波浪号 (~) 是通配符。 Replace 函数应该是相同的,但我无法让这些通配符与该函数一起使用。

您可以尝试添加以下语句:

Vtemplatebody = Replace(Vtemplatebody,"~emp~","xxx")
Vtemplatebody = Replace(Vtemplatebody,"~awd~","yyy")
Debug.Print Vtemplatebody

在这些语句中,“xxx”和“yyy”代表ws.Cells(i, "f")ws.Cells(i, "h") 中的值。这些语句将允许您尝试不同的替换并在立即窗口中查看效果。有了这样的陈述,就可以很容易地尝试不同的值并解决一些一旦发现就会很明显的微妙问题。

另一种选择是用百分比替换波浪号。百分比不是通配符,因此使用它作为分隔符可以消除通配符是问题的任何可能性。

需要多少次替换。您的方法可以进行一两次替换,但如果进行多次替换,您的方法就会变得很辛苦。如果有帮助,我可以向您展示一种更好的技术。

【讨论】:

  • 抱歉延迟回复。我确实对您建议的线路进行了更改。如果有多个替换,我很想看到更好的技术。谢谢。
  • 我可以通过使用 ActiveInspector 对象使其工作。
  • 期待你提到的更好的技术。
  • @user1955215。我以为我添加了一条指向我的新答案的评论,但显然没有。我的第二个答案描述了五种不同的方法,我会在适当的时候使用它们。
【解决方案2】:

大多数编程任务都可以通过几种不同的方法来解决。对于任何给定任务,您如何选择最佳方法?

如果您正在编写一个每天处理数百万笔交易的交易处理器,那么节省的每一微秒处理时间都是宝贵的。程序员花几个小时的时间来稍微加快这个过程可能是值得的。但是,如果您正在编写事务处理器,那么选择 VBA 将是您的第一个错误。 VBA 并不像某些人认为的那样慢,但它的设计目的是易于编写而不是快速执行。对于每天只执行一次的宏或者用户输入后隐藏了任何缓慢的宏,尽量减少程序员的工作量是首要任务。

一种算法可能比另一种更有效。我认为选择最合适的算法是设计决策而不是编程决策。

在衡量程序员时间时,需要考虑:

  • 原始程序员编写和调试代码需要多长时间。
  • 维护程序员需要多长时间才能理解代码。
  • 维护程序员需要多长时间来更新和调试代码以满足新要求。

我有时会看到我认为很聪明但很难理解的代码。最初的程序员花了多长时间才弄好?每个维护程序员需要多长时间才能理解代码的作用?很少有代码只写一次就永远不会更新。需求可能会发生变化,并且必须审查和更新代码。

考虑:

strFind = "Dear ~emp~"                           '
strNew = "Dear " & ws.Cells(i, "f")              ' Your code
.htmlbody = Replace(.htmlbody, strFind, strNew)  '

strFind = "~emp~"                                ' The code a maintenance
strNew = ws.Cells(i, "f")                        ' programmer
.htmlbody = Replace(.htmlbody, strFind, strNew)  ' might expect

为什么您在第一个 find 和 new 值中都包含“Dear”,但对第二个 find 和 new 值没有做类似的事情?如果有原因,请添加注释,否则每个维护程序员都会花时间想知道为什么。最终有人会删除"Dear "。如果"Dear " 很重要,这将导致以后出现问题。

工作表的“f”和“h”列是什么?在这种情况下,它可能无关紧要。维护程序员不太可能在不查看工作表的情况下修改代码,并且代码中的每一列可能只有一个引用。当有很多列和对这些列的大量引用时,就会出现问题。许多年前,我是一些代码的维护程序员,其中很多引用了第 5 列和第 6 列。由于超出我的工资等级的原因,这两列被交换了。引用列的代码中的每个“5”都必须替换为“6”,引用列的代码中的每个“6”都必须替换为“5”。这是一场噩梦,特别是因为代码中有很多其他的 5 和 6。我避免在代码中使用文字。我更喜欢这样的东西:

Const ColSalutation As String = “f”
:          :          :          :          :
strNew = ws.Cells(i, ColSalutation).Value

这有两个好处:(1) 如果列位置发生变化,则对代码的一次修改以及对列的所有引用都已更改;(2) 有意义的名称,例如 ColSalutation,使代码更易于阅读。

考虑:

strFind1 = "~awd~"
strNew1 = ws.Cells(i, "h")
.htmlbody = Replace(.htmlbody, strFind1, strNew1)

为什么是strFind1strNew1strFindstrNew 中的值将不再需要,因此这些变量可供使用。如果有原因,请记录下来,以免让维护程序员担心。

为什么不:.htmlbody =Replace(.htmlbody, "~awd~", ws.Cells(i, "h"))`?

在某些情况下,我认为将值移动到临时变量会使代码更清晰。我不确定它是否使这里的代码更清晰。

下面我介绍五个子例程,展示不同的方法来满足您的要求。我会就每种方法何时最合适提出建议。

我相信最简单的方法是创建一个新的启用宏的工作簿,其中包含两个工作表:“数据”和“替换”。宏 Prepare 会将值放入其他宏所期望的工作表中。

Sub Prepare()

  With Worksheets("Data")
    .Range("F5:I5").Value = Array("Jane", DateSerial(2020, 2, 1), 100, TimeSerial(12, 0, 0))
    .Range("G5").NumberFormat = "dddd mmmm d, yyyy"
    .Range("H5").NumberFormat = "$#,##0.00"
    .Range("I5").NumberFormat = "h:mm"
    .Columns.AutoFit
  End With
  With Worksheets("Replace")
    .Range("A1:B1").Value = Array("Original Value", "Column")
    .Range("A2:B2").Value = Array("%emp%", "f")
    .Range("A3:B3").Value = Array("%awd%", "h")
    .Range("A4:B4").Value = Array("%xyz%", "g")
    .Range("A5:B5").Value = Array("%uvw%", "i")
    .Columns.AutoFit
  End With

End Sub

Approach1 是基于你的方法:

Sub Approach1()

  Dim HtmlBody As String
  Dim i As Long
  Dim ws As Worksheet
  Dim strFind As String
  Dim strNew As String

  Set ws = Worksheets("Data")
  i = 5
  HtmlBody = "<body>Dear %emp%<p>I am pleased to inform you of an award of " & _
             "<b>%awd%</b> which you will receive on %xyz% at %uvw%.<p>Yours<p>" & _
             "John Doe</body>  "

  strFind = "Dear %emp%"
  strNew = "Dear " & ws.Cells(i, "f")
  HtmlBody = Replace(HtmlBody, strFind, strNew)

  strFind = "%awd%"
  strNew = ws.Cells(i, "h")
  HtmlBody = Replace(HtmlBody, strFind, strNew)

  strFind = "%xyz%"
  strNew = ws.Cells(i, "g")
  HtmlBody = Replace(HtmlBody, strFind, strNew)

  strFind = "%uvw%"
  strNew = ws.Cells(i, "i")
  HtmlBody = Replace(HtmlBody, strFind, strNew)

  Debug.Print HtmlBody

End Sub

有了这个子例程,以及所有后续的子例程,我已经开始使用一些代码来初始化在您的代码 sn-p 之前初始化的变量。我遵循了自己的建议并用百分比替换波浪号。我还将替换的数量从两个增加到四个。工作表中的显示值为:

Row|  F |             G           |   H   |  I  |
   |----+-------------------------+-------+-----|
  5|Jane|Saturday February 1, 2020|$100.00|12:00|
   |----+-------------------------+-------+-----|

对于方法 1,每次替换需要三个新语句。复制上次替换然后修改副本以进行新替换很容易。修改副本也很容易出错,而且通常很难发现这些错误。这种方法的替换数量限制是个人选择,但我不喜欢这种方法,我的限制是两个或三个。

方法2几乎相同,只是我没有使用临时变量:

Sub Approach2()

  Dim HtmlBody As String
  Dim i As Long
  Dim ws As Worksheet

  Set ws = Worksheets("Data")
  i = 5
  HtmlBody = "<body>Dear %emp%<p>I am pleased to inform you of an award of " & _
             "<b>%awd%</b> which you will receive on %xyz% at %uvw%.<p>Yours<p>" & _
             "John Doe</body>  "

  HtmlBody = Replace(HtmlBody, "%emp%", ws.Cells(i, "f").Text)
  HtmlBody = Replace(HtmlBody, "%awd%", ws.Cells(i, "h").Text)
  HtmlBody = Replace(HtmlBody, "%xyz%", ws.Cells(i, "g").Text)
  HtmlBody = Replace(HtmlBody, "%uvw%", ws.Cells(i, "i").Text)

  Debug.Print HtmlBody

End Sub

对于方法 2,我已将您的三个语句压缩为一个。如果我能实现这样的目标,并且易于查看和查看差异,那么我对替换数量的限制会高很多。

注意,“.Text”。您只需指定单元格,例如:ws.Cells(i, "f")。这意味着您将获得单元格的默认属性,即“值”。 Value 属性适用于字符串,但对于数字和日期,您将获得未格式化的值。对于时间,您将获得 Excel 或 Outlook 用来表示时间的数字。 Text 属性为您提供显示值。比较宏 Approach1Approach2 的输出,看看我的意思。

使用方法 3,我将每个替换的值加载到一个数组中,然后循环遍历该数组。关键语句是:

OrigValuesAndCols = Array("%emp%", "f", _
                          "%awd%", "h", _
                          "%xyz%", "g", _
                          "%uvw%", "i")

OrigValuesAndCols 是一个变体。函数Array 有效地将其转换为具有八个元素的数组。每对元素都是一个查找值和一个列号。我编写了一个循环,它提取四对并依次执行四个替换中的每一个。如果您对Variants 和/或函数Array 不熟悉,这种方法可能需要仔细研究才能理解。仔细研究会得到回报,因为这是我经常使用的强大技术。当我描述方法 4 时,我会说更多。

Sub Approach3()

  Dim Col As String
  Dim HtmlBody As String
  Dim i As Long
  Dim Inx As Long
  Dim OrigValuesAndCols As Variant
  Dim ws As Worksheet

  Set ws = Worksheets("Data")
  i = 5
  HtmlBody = "<body>Dear %emp%<p>I am pleased to inform you of an award of " & _
             "<b>%awd%</b> which you will receive on %xyz% at %uvw%.<p>Yours<p>" & _
             "John Doe</body>  "

  OrigValuesAndCols = Array("%emp%", "f", _
                            "%awd%", "h", _
                            "%xyz%", "g", _
                            "%uvw%", "i")

  For Inx = LBound(OrigValuesAndCols) To UBound(OrigValuesAndCols) Step 2
    Col = OrigValuesAndCols(Inx + 1)
    HtmlBody = Replace(HtmlBody, OrigValuesAndCols(Inx), ws.Cells(i, Col).Text)
  Next

  Debug.Print HtmlBody

End Sub

方法 4 类似于方法 3,但我创建了两个数组:

OrigValues = VBA.Array("%emp%", "%awd%", "%xyz%", "%uvw%")
NewValues = VBA.Array(ws.Cells(i, "f").Text, ws.Cells(i, "h").Text, _
                      ws.Cells(i, "g").Text, ws.Cells(i, "i").Text)

函数VBA.Array保证数组的下限为0。使用函数Array,默认下限为0,但Option Base statement可以将其更改为1。我从未见过有人使用@ 987654349@ 但是,如果下限可以改变,我使用函数LBound 来获取下限。对于方法 4,我的循环语句是:For Inx = 0 To UBound(NewValues),因为我知道下限必须为零。使用方法 3,我的循环语句是:For Inx = LBound(OrigValuesAndCols) To UBound(OrigValuesAndCols) Step 2。我允许下限改变。 Step 2 表示循环步骤 Inx 每次重复 2。

使用方法 3,每对的第二个值是列代码,替换语句将其转换为单元格引用。使用方法 4,数组 NewValues 包含单元格显示值。在一种方法中使用列代码,在另一种方法中使用单元格显示值,表明我可以。我认为使用列代码会使数组更容易理解,但您可能不同意。有时我发现一个数组更容易,有时我发现两个数组更容易。大多数时候我发现一个数组更容易,因为检查一对匹配的两个元素更容易。使用两个数组可能很难检查 OrigValues(20) 和 NewValues(20) 是否匹配。

Sub Approach4()

  Dim HtmlBody As String
  Dim i As Long
  Dim Inx As Long
  Dim NewValues As Variant
  Dim OrigValues As Variant
  Dim ws As Worksheet

  Set ws = Worksheets("Data")
  i = 5
  HtmlBody = "<body>Dear %emp%<p>I am pleased to inform you of an award of " & _
             "<b>%awd%</b> which you will receive on %xyz% at %uvw%.<p>Yours<p>" & _
             "John Doe</body>  "

  OrigValues = VBA.Array("%emp%", "%awd%", "%xyz%", "%uvw%")
  NewValues = VBA.Array(ws.Cells(i, "f").Text, ws.Cells(i, "h").Text, _
                        ws.Cells(i, "g").Text, ws.Cells(i, "i").Text)

  For Inx = 0 To UBound(NewValues)
    HtmlBody = Replace(HtmlBody, OrigValues(Inx), NewValues(Inx))
  Next

  Debug.Print HtmlBody

End Sub

方法 3 和方法 4 都可以用于合理数量的替换,但它们可能会变得笨拙。它们还具有必须由程序员进行任何更改的缺点。对于方法 5,我添加了一个新的工作表“替换”与值:

Row|       A      |   B  |
   |--------------+------|
  1|Original Value|Column|
   |--------------+------|
  2|%emp%         |f     |
   |--------------+------|
  3|%awd%         |h     |
   |--------------+------|
  4|%xyz%         |g     |
   |--------------+------|
  5|%uvw%         |i     |
   |--------------+------| 

当我看到可以更容易地在工作表中创建然后加载到数组中的代码构建数组时,我有时会感到惊讶。替换次数仅受每个工作表的行数限制,用户可以根据需要维护此表。

使用这种工作表方法,代码变为:

Sub Approach5()

  Dim ColData As String
  Dim HtmlBody As String
  Dim NewValue As String
  Dim OrigValue As String
  Dim RowData As Long
  Dim RowRplcCrnt As Long
  Dim RowRplcLast As Long
  Dim wsData As Worksheet
  Dim wsRplc As Worksheet

  Set wsData = Worksheets("Data")
  Set wsRplc = Worksheets("Replace")
  RowData = 5
  HtmlBody = "<body>Dear %emp%<p>I am pleased to inform you of an award of " & _
             "<b>%awd%</b> which you will receive on %xyz% at %uvw%.<p>Yours<p>" & _
             "John Doe</body>  "

  With wsRplc
    RowRplcLast = .Cells(, Rows.Count, 1).End(xlUp).Row
  End If

  For RowRplcCrnt = 2 To rowrpllast
    With wsRplc
      OrigValue = .Cells(RowRplcCrnt, 1).Value
      ColData = .Cells(RowRplcCrnt, 2).Value
    End With
    NewValue = ws.Cells(RowData, Col).Text
    HtmlBody = Replace(HtmlBody, OrigValue, NewValue)
  Next

  Debug.Print HtmlBody

End Sub

我已经在操作宏中使用了这些方法中的每一种。在每种情况下,它们都不是最好的方法。我希望你能理解我是如何为每个问题选择最佳方法的。关键问题是:对于原始程序员来说,哪种方法最容易创建和记录,并且所有未来的维护程序员都将尽可能容易地理解。

【讨论】:

    猜你喜欢
    • 2013-04-15
    • 2016-07-12
    • 2019-11-21
    • 1970-01-01
    • 1970-01-01
    • 2013-08-29
    • 2018-11-04
    • 1970-01-01
    • 2011-08-23
    相关资源
    最近更新 更多