excel - 在 Excel 中使用 VBA 从 Word 文档的标题中查找/替换文本
问题描述
我对 Excel 中的 VBA 编码比较陌生。我已经修改了这个 VBA 代码以供我使用,以便用 Excel 工作表中的内容替换所有标记的文本。这适用于 word 文档中的主要内容。我唯一的问题是它没有在 Word 文档的标题中搜索/替换文本。是否有人对编辑代码以查找和替换标题中的文本有任何建议?我确信这很简单,比如定义正确的对象,但我无法弄清楚。谢谢!
Dim CustRow, CustCol, TemplRow As Long
Dim DocLoc, TagName, TagValue, TemplName, FileName As String
Dim CurDt, LastAppDt As Date
Dim WordDoc, WordApp As Object
Dim WordContent, WordHeaderFooter As Word.Range
With Sheet106
TemplRow = .Range("B3").Value 'Set Template Row
TemplName = .Range("J3").Value 'Set Template Name
DocLoc = .Range("E" & TemplRow).Value 'Word Document Filename
'Open Word Template
On Error Resume Next 'If Word is already running
Set WordApp = GetObject("Word.Application")
If Err.Number <> 0 Then
'Launch a new instance of Word
Err.Clear
'On Error GoTo Error_Handler
Set WordApp = CreateObject("Word.Application")
WordApp.Visible = True 'Make the application visible to the user
End If
CustRow = 4
Set WordDoc = WordApp.Documents.Open(FileName:=DocLoc, ReadOnly:=False) 'Open Template
For CustCol = 16 To 180 'Move Through all Columns
TagName = .Cells(3, CustCol).Value 'Tag Name
TagValue = .Cells(CustRow, CustCol).Value 'Tag Value
With WordDoc.Content.Find
.Text = TagName
.Replacement.Text = TagValue
.Wrap = wdFindContinue
.Execute Replace:=wdReplaceAll 'Find & Replace all instances
End With
Next CustCol
If .Range("J1").Value = "PDF" Then
FileName = ThisWorkbook.Path & "\" & .Range("Q" & CustRow).Value & _
"_" & .Range("P" & CustRow).Value & ".pdf" 'Create full filename & Path with current workbook location, Last Name & First Name
WordDoc.ExportAsFixedFormat OutputFileName:=FileName, ExportFormat:=wdExportFormatPDF
WordDoc.Close False
Else: 'If Word
FileName = ThisWorkbook.Path & "\" & .Range("Q" & CustRow).Value _
& "_" & .Range("P" & CustRow).Value & ".docx"
WordDoc.SaveAs FileName
End If
End With
End Sub
解决方案
Tim Williams 和我都建议查看 Jonathan West、Peter Hewitt、Doug Robbins 和 Greg Maxey 的MVP 网页。以下是部分引文。
这是 Word 代码,因此您需要将其标记到 WordDoc 对象而不是 ActiveDocument。
在任何地方查找或替换文本的完整代码有点复杂。因此,让我们一步一步来更好地说明这个过程。在许多情况下,更简单的代码足以完成工作。
步骤1
以下代码循环遍历活动文档中的每个 StoryRange,并将指定的 .Text 替换为 .Replacement.Text:
Sub FindAndReplaceFirstStoryOfEachType()
Dim rngStory As Range
For Each rngStory In ActiveDocument.StoryRanges
With rngStory.Find
.Text = "find text"
.Replacement.Text = "I'm found"
.Wrap = wdFindContinue
.Execute Replace:=wdReplaceAll
End With
Next rngStory
End Sub
(对于那些已经熟悉 VBA 的人请注意:如果您使用 Selection.Find,则必须指定所有查找和替换参数,例如 .Forward = True,否则这些设置取自“查找和替换”对话框的当前设置,这是“粘性的”,如果使用 [Range].Find 则没有必要 - 如果您未在代码中指定参数值,则参数使用其默认值)。
上面的简单宏有缺点。它只作用于 11 个 StoryType 中的每一个的“第一个”StoryRange(即第一个标题、第一个文本框等)。虽然一个文档只有一个 wdMainTextStory StoryRange,但它可以在其他一些 StoryTypes 中有多个 StoryRanges。例如,如果文档包含带有未链接的页眉和页脚的部分,或者如果它包含多个文本框,则这些 StoryType 将有多个 StoryRanges,并且代码不会对第二个和后续 StoryRanges 起作用。更复杂的是,如果您的文档包含未链接的页眉或页脚,并且其中一个页眉或页脚为空,则 VBA 可能无法“跳转”该空页眉或页脚并处理后续页眉和页脚。
第2步
为了确保代码作用于每个 StoryType 中的每个 StoryRange,您需要:
Make use of the NextStoryRange method Employ a bit of VBA "trickery" as provided by Peter Hewett to bridge any empty unlinked headers and footers.
Public Sub FindReplaceAlmostAnywhere()
Dim rngStory As Word.Range
Dim lngJunk As Long
'Fix the skipped blank Header/Footer problem as provided by Peter Hewett
lngJunk = ActiveDocument.Sections( 1 ).Headers( 1 ).Range.StoryType
'Iterate through all story types in the current document
For Each rngStory In ActiveDocument.StoryRanges
'Iterate through all linked stories
Do
With rngStory.Find
.Text = "find text"
.Replacement.Text = "I'm found"
.Wrap = wdFindContinue
.Execute Replace:=wdReplaceAll
End With
'Get next linked story (if any)
Set rngStory = rngStory.NextStoryRange
Loop Until rngStory Is Nothing
Next
End Sub
还有一个问题。就像使用查找和替换实用程序一样,上面的代码可能会错过嵌套在不同 StoryType/StoryRange 中的一个 StoryType/StoryRange 中包含的任何文本。虽然 wdMainTextStory StoryRange 中的嵌套 StoryType/StoryRange 不会出现此问题,但它确实会出现在页眉和页脚类型 StoryRanges 中。一个示例是位于页眉或页脚中的文本框。
第 3 步
幸运的是,Jonathan West 为这种嵌套的 StoryRanges 问题提供了一种解决方法。解决方法利用了文本框和其他绘图形状包含在文档的 ShapeRange 集合中这一事实。因此,我们可以在六个页眉和页脚 StoryRanges 中的每一个中检查 ShapeRange 是否存在 Shapes。如果找到一个 Shape,然后我们检查每个 Shape 是否存在文本,最后,如果 Shape 包含文本,我们将搜索范围设置为该 Shape 的 .TextFrame.TextRange。
这个最终的宏包含在文档中“任意位置”查找和替换文本的所有代码。添加了一些增强功能,以便更轻松地应用所需的查找和替换文本字符串。
注意:在粘贴之前将代码文本转换为纯文本很重要:如果直接从 Web 浏览器粘贴,空格被编码为不间断空格,这不是 VBA 的“空格”,会导致编译或运行-时间错误。另外:请注意此代码中的长行。当您将此代码粘贴到 VBA 编辑器中时,您粘贴的任何地方都应该没有红色可见。如果有,请尝试小心地将顶部的红线与其下方的红线连接起来(不要删除任何可见字符。
Public Sub FindReplaceAnywhere()
Dim rngStory As Word.Range
Dim pFindTxt As String
Dim pReplaceTxt As String
Dim lngJunk As Long
Dim oShp As Shape
pFindTxt = InputBox("Enter the text that you want to find." _
, "FIND" )
If pFindTxt = "" Then
MsgBox "Cancelled by User"
Exit Sub
End If
TryAgain:
pReplaceTxt = InputBox( "Enter the replacement." , "REPLACE" )
If pReplaceTxt = "" Then
If MsgBox( "Do you just want to delete the found text?", _
vbYesNoCancel) = vbNo Then
GoTo TryAgain
ElseIf vbCancel Then
MsgBox "Cancelled by User."
Exit Sub
End If
End If
'Fix the skipped blank Header/Footer problem
lngJunk = ActiveDocument.Sections( 1 ).Headers( 1 ).Range.StoryType
'Iterate through all story types in the current document
For Each rngStory In ActiveDocument.StoryRanges
'Iterate through all linked stories
Do
SearchAndReplaceInStory rngStory, pFindTxt, pReplaceTxt
On Error Resume Next
Select Case rngStory.StoryType
Case 6 , 7 , 8 , 9 , 10 , 11
If rngStory.ShapeRange.Count > 0 Then
For Each oShp In rngStory.ShapeRange
If oShp.TextFrame.HasText Then
SearchAndReplaceInStory oShp.TextFrame.TextRange, _
pFindTxt, pReplaceTxt
End If
Next
End If
Case Else
'Do Nothing
End Select
On Error GoTo 0
'Get next linked story (if any)
Set rngStory = rngStory.NextStoryRange
Loop Until rngStory Is Nothing
Next
End Sub
Public Sub SearchAndReplaceInStory(ByVal rngStory As Word.Range, _
ByVal strSearch As String , ByVal strReplace As String )
With rngStory.Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = strSearch
.Replacement.Text = strReplace
.Wrap = wdFindContinue
.Execute Replace:=wdReplaceAll
End With
End Sub
推荐阅读
- apache-spark - SAP Vora 2.1 是否需要 Hadoop/Spark 集群?可以使用 PySpark 吗?
- java - 使用 Apache Camel 和 spring-ws 组件调用基于 SOAP 的服务
- css - 如何在不使用自动调整大小的情况下制作灵活的按钮
- c++ - 致命错误:iostream:没有这样的文件或目录#include
- asp.net-mvc - AbortRequest 和 Forbidden 有什么区别
- python - 如何根据其他熊猫数据框更新系列
- javascript - 使用 Node JS 事件创建自己的状态管理器
- python-3.x - 如何通过对数据趋势应用线性回归来找出斜率值?
- javascript - 不推荐在 ref 属性中使用字符串文字
- cmake - pkg-config 使用了错误的前缀