首页 > 解决方案 > 用 VBA for Word 查找和替换会覆盖以前的样式

问题描述

我正在编写一个 VBA 脚本来从已经定义的模板生成 word 文档。在其中,我需要能够编写标题以及每个标题的正文。作为一个小例子,我有一个只包含<PLACEHOLDER>. 对于我需要编写的每个标题和正文,我使用 VBA 中的查找<PLACEHOLDER>和替换功能来查找并将其替换为标题名称、换行符,然后<PLACEHOLDER>再次替换。重复此过程,直到写入每个标题名称和正文,然后将<PLACEHOLDER>结尾替换为换行符。

文本替换工作正常,但我指定的样式会被下一次替换调用覆盖。这导致我刚刚替换的所有内容都具有我上次调用替换函数的样式。

VBA 代码(运行main

Option Explicit

Sub replace_stuff(search_string As String, replace_string As String, style As Integer)
    With ActiveDocument.Range.Find
        .Text = search_string
        .Replacement.Text = replace_string
        .Replacement.style = style
        .Forward = True
        .Wrap = wdFindContinue
        .Format = True
        .MatchWholeWord = False
        .MatchSoundsLike = False
        .MatchAllWordForms = False
        .Execute Replace:=wdReplaceAll
    End With
End Sub

Sub main()
    Dim section_names(2) As String
    section_names(0) = "Introduction"
    section_names(1) = "Background"
    section_names(2) = "Conclusion"

    Dim section_bodies(2) As String
    section_bodies(0) = "This is the body text for the introduction! Fetched from some file."
    section_bodies(1) = "And Background... I have no issue fetching data from the files."
    section_bodies(2) = "And for the conclusion... But I want the styles to 'stick'!"

    Dim i As Integer
    For i = 0 To 2
        ' Writes each section name as wsStyleHeading2, and then the section body as wdStyleNormal
        Call replace_stuff("<PLACEHOLDER>", section_names(i) & Chr(11) & "<PLACEHOLDER>", wdStyleHeading2)
        Call replace_stuff("<PLACEHOLDER>", section_bodies(i) & Chr(11) & "<PLACEHOLDER>", wdStyleNormal)
    Next i
    Call replace_stuff("<PLACEHOLDER>", Chr(11), wdStyleNormal)

End Sub

输入文档:一个word文档,里面只有一个<PLACEHOLDER>

<PLACEHOLDER>

预期输出: 我希望每个标题都以我指定的样式显示,并且可以从导航窗格中查看,如下所示:

预期产出

实际输出:但是我实际得到的是这样的wdStyleNormal风格:

实际的

我认为可以通过在每个样式转换之间插入一个段落分隔符来解决问题,但是当我尝试使用vbCrLForChr(10) & Chr(13)vbNewLine代替chr(11)我现在使用的时,每一行都以这样的盒装问号开头:

在此处输入图像描述

标签: vbams-word

解决方案


在另一个答案的评论中更新讨论。下面描述的问题适用于 Word 2016 及更早版本。从Office 365(可能还有 Word 2019,但尚未确认)开始,Replace行为已更改为将 ANSI 13“转换”为“真实”段落标记,因此不会出现问题中的问题。

回答

奇怪的格式化行为的原因是使用Chr(11),它插入一个新行(Shift + Enter)而不是一个新段落。因此,应用于此文本任何部分的段落样式以相同的样式格式化整个文本。

在这种特殊情况下(使用Replace),vbCr或等效的Chr(13)也不起作用,因为这些不是真正的 Word 原生段落。段落不仅仅是 ANSI 代码 13 - 它包含段落格式信息。因此,在代码运行时,Word 并没有真正将它们识别为真正的段落标记,并且段落样式分配被应用于“所有内容”。

起作用的是使用 string ^p,它在 Word 的 Find/Replace 中是完整段落标记的“别名”。因此,例如:

replace_stuff "<PLACEHOLDER>", section_names(i) & "^p" & "<PLACEHOLDER>", wdStyleHeading2
replace_stuff "<PLACEHOLDER>", section_bodies(i) & "^p" & "<PLACEHOLDER>", wdStyleNormal

但是,与为每个新项目插入占位符并使用查找/替换将占位符替换为文档内容相比,有一种更有效的方法来构建文档。更传统的方法是使用一个Range对象(把它想象成一个不可见的选择)......

将内容分配给Range、格式化、折叠(如按右箭头进行选择)并重复。这是一个示例,它返回与问题中的(更正的)代码相同的结果:

Sub main()
  Dim rng As Range

  Set rng = ActiveDocument.content
  Dim section_names(2) As String
  section_names(0) = "Introduction"
  section_names(1) = "Background"
  section_names(2) = "Conclusion"

  Dim section_bodies(2) As String
  section_bodies(0) = "This is the body text for the introduction! Fetched from some file."
  section_bodies(1) = "And Background... I have no issue fetching data from the files."
  section_bodies(2) = "And for the conclusion... But I want the styles to 'stick'!"

  Dim i As Integer
  For i = 0 To 2
    BuildParagraph section_names(i), wdStyleHeading2, rng
    BuildParagraph section_bodies(i), wdStyleNormal, rng
  Next i
End Sub

Sub BuildParagraph(para_text As String, para_style As Long, rng As Range)
        rng.Text = para_text
        rng.style = para_style
        rng.InsertParagraphAfter
        rng.Collapse wdCollapseEnd
End Sub

推荐阅读