我正在使用包含各种占位符的.oft(Outlook模板)文件。我想用字符串替换占位符
oft文件中的占位符为:
亲爱的〜emp〜 〜awd〜
我正在使用以下代码进行替换:
.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)
第一个替换viz〜emp〜正正确发生,即字符串被ws.Cells(i,“ f”)中的值替换。但是第二个〜awd〜并没有替换为strNew1中的值。
我做的事情有问题吗?还是可以通过其他方式解决?
答案 0 :(得分:0)
您运行了此代码吗?如果是这样,在哪里?我希望编译器将.htmlbody
转换为.HtmlBody
。
您给我们提供的代码太少了,但是我怀疑通配符。使用替换方法时,星号(*),问号(?)和波浪号(〜)是通配符。替换功能应该是相同的,但是我无法使用任何这些通配符来使用该功能。
您可以尝试添加以下语句:
Vtemplatebody = Replace(Vtemplatebody,"~emp~","xxx")
Vtemplatebody = Replace(Vtemplatebody,"~awd~","yyy")
Debug.Print Vtemplatebody
在这些语句中,“ xxx”和“ yyy”表示ws.Cells(i, "f")
和ws.Cells(i, "h")
中的值。这些语句将允许您尝试不同的替换,并在立即窗口中看到效果。使用这样的语句,可以很容易地尝试使用不同的值,并解决一些细微的问题,一旦发现,这些问题就将显而易见。
另一个选择是将波浪号替换为百分比。百分号不是通配符,因此将其用作定界符将消除通配符成为问题的任何可能性。
需要多少个这些替换。您的方法可以用一两个替代品很好,但要进行许多替代却很难。如果有帮助,我可以向您展示一种更好的技术。
答案 1 :(得分:0)
大多数编程任务可以通过几种不同的方法来解决。您如何选择对任何给定任务最好的方法?
如果您要编写一个每天要处理数百万个事务的事务处理器,那么节省的每一微秒的处理时间都是宝贵的。可能需要花几个小时的时间来使过程略微加快一些时间。但是,如果您正在编写事务处理器,那么选择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
为什么在第一个发现和新值中都包含“亲爱的”,而对第二个发现和新值却不做任何相似的事情?如果有原因,请添加注释,否则每个维护程序员都会花时间思考为什么。最终,有人会删除"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)
为什么使用strFind1
和strNew1
? strFind
和strNew
中的值将不再需要,因此可以使用这些变量。如果有原因,请记录下来,以免引起维护程序员的注意。
为什么不:.htmlbody =
替换(.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
使用此子例程以及所有后续子例程,我从一些代码开始,以初始化在您的代码片段之前初始化的变量。我遵循了自己的建议,并用%代替了波浪号。我还将替换的数量从两个增加到四个。工作表中的显示值为:
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属性为您提供显示值。比较宏Approach1
和Approach2
的输出以了解我的意思。
使用方法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。从未见过有人使用Option Base statement
,但是,如果下界可以更改,我将使用函数LBound
来获得下界。对于方法4,我的循环语句为:For Inx = 0 To UBound(NewValues)
,因为我知道下限必须为零。使用方法3时,我的循环语句为:For Inx = LBound(OrigValuesAndCols) To UBound(OrigValuesAndCols) Step 2
。我允许下限进行更改。 Step 2
表示循环步骤Inx
每次重复2。
使用方法3,每对的第二个值是列代码,而replace语句将其转换为单元格引用。使用方法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,我添加了一个新的工作表“ Replace”,其值如下:
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
我在操作宏中使用了所有这些方法。在任何情况下,它们都不是最佳方法。希望您能了解如何为每个问题选择最佳方法。关键问题是:对于原始程序员而言,哪种方法最容易创建和记录文档,以及所有未来的维护程序员将尽可能容易地理解哪种方法。