VBA WORD: 查找具有特定样式的段落
VBA WORD: find a paragraph that has a specific style
我正在尝试在 VBA 中编写代码,我将在其中获取具有特定样式的段落的编号(假设为标题 1)。
我正在经历一个循环,不幸的是我收到这样的错误:
"Object variable or With block variable not set"
这是我的代码:
Public Function FindParagraph(ByVal pStyle As String) As Integer
Dim doc As Document
Dim pNum As Integer
Set doc = ActiveDocument
For pNum = 1 To doc.Paragraphs.Count
Debug.Print pNum, doc.Paragraphs(pNum).Range.Style
If doc.Paragraphs(pNum).Range.Style = pStyle Then
FindParagraph = pNum
Exit For
End If
Next pNum
End Function
Sub DoSth()
Dim i As Integer
i = FindParagraph("Heading 1")
Debug.Print i
End Sub
调试器显示问题出在这一行:
pStyle = doc.Paragraphs(i).Range.Style
实际上,我正在查看我的 Word 文档,它是 Table 目录的第一行。
你知道为什么会这样吗?
您提供的代码无法编译。它在
处给出错误
While Not (IsEmpty(pStyle))
因为方法 IsEmpty 只能用于 Variant 类型,而您分配给 pStyle 的类型是字符串。为了实现您的意图,您需要将此行更改为
While Not pStyle = vbNullString
已更新以提供修改后的功能
Sub TestFindParagraph()
Dim IsFound As Boolean
IsFound = FindParagraph(ActiveDocument.StoryRanges(wdMainTextStory), "Heading 1")
End Sub
Public Function FindParagraph(ByVal SearchRange As Word.Range, ByVal ParaStyle As String) As Long
Dim ParaIndex As Long
For ParaIndex = 1 To SearchRange.Paragraphs.Count
If doc.Paragraphs(ParaIndex).Range.Style = ParaStyle Then
FindParagraph = ParaIndex
Exit Function
End If
Next
End Function
更新 2020-Apr-15 解决目录问题
当段落是 Table 的内容字段时,OP 和我自己发布的代码都失败了。发生此故障是因为 VBA 个对象的默认成员功能。
对象的默认成员是在没有限定方法的情况下给定对象实例时调用的方法。此功能有助于简化代码,但可能会导致类似于我们遇到的奇怪错误。
在 Word 中,样式是具有大量属性和方法的对象。 Style 的默认方法是 NameLocal(请参阅 Word.Style 的对象浏览器),其中 return 是一个包含字符串的 Variant(请参阅本地 windows 中样式的类型)。
因此,即使 pStyle 被定义为 String 类型,VBA 强制转换也允许将 variant/string 分配给 String pStyle,并且一切似乎都正常。
但是,对于 TOC 字段,Word 似乎没有 return 包装 TOC 字段的样式,而是 returns 'nothing' 的值,即不是目录的样式。不能将任何内容(与 vbNullString 不同)分配给字符串,因此会发生错误。
上面遇到的问题似乎有两种解决方案。
更改代码以对我们需要的信息使用正确的语法。即 Style.NameLocal。不幸的是,这个解决方案也会失败,因为我们不能在什么都不是的对象上调用方法 (NameLocal)。
将 pStyle 的变量类型从 String 更改为 Variant。变体类型可以保存对象,因此可以保存当段落是 TOC 字段时生成的任何值。
解决方案 2 似乎工作正常,但从纯粹主义者的角度来看,您将有一个中间变体变量,它捕获 Style 的结果,然后在将字符串值或 vbNullString 分配给 pStyle 之前测试该变体。
最终更新 2020 年 4 月 15 日
不幸的是,我因为一些紧急工作被 F 夫人叫走了,所以忘记添加最后的内容。
可以轻松避免默认成员陷阱。这要归功于 Rubberduck 的人们所做的出色工作。
VBA 的 Rubberduck 插件(免费)作为其众多才能之一,对 VBA 进行了更严格的代码分析(代码检查)。其中一项检查是在代码中使用默认成员的任何地方发出警告。 Rubberduck 确实消除了编写 VBA 代码时的大量痛苦,因为它可以帮助您准确理解您在代码中所做的假设(您没有意识到自己所做的假设)...
Word中的段落不可能没有段落样式,所以测试一个段落是否完全没有任何样式是没有意义的。
此外,循环遍历所有段落的效率远低于使用“查找”。例如,以下代码检索每个标题 1 的段落索引#:
Sub Demo()
Application.ScreenUpdating = False
Dim Rng As Range, i As Long
With ActiveDocument.Range
Set Rng = .Duplicate
With .Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = ""
.Replacement.Text = ""
.Style = wdStyleHeading1
.Format = True
.Forward = True
.Wrap = wdFindStop
.MatchWildcards = False
.Execute
End With
Do While .Find.Found
i = i + 1
Rng.End = .Duplicate.End
MsgBox Rng.Paragraphs.Count
'The next If ... End If block is only needed if the Found content might be in a table
If .Information(wdWithInTable) = True Then
If .End = .Cells(1).Range.End - 1 Then
.End = .Cells(1).Range.End
.Collapse wdCollapseEnd
If .Information(wdAtEndOfRowMarker) = True Then
.End = .End + 1
End If
End If
End If
'The next line is only needed if the Found content might include the document's final paragraph break
If .End = ActiveDocument.Range.End Then Exit Do
.Collapse wdCollapseEnd
.Find.Execute
Loop
End With
Application.ScreenUpdating = True
MsgBox i & " instances found."
End Sub
更多代码,但速度更快。
而是for i=1 to ....Count
尝试 for each
循环以获得更好的效率
Private Sub StylesCount()
Dim p As Paragraph
Dim story As Range
Dim counter As Long
For Each story In ActiveDocument.StoryRanges
For Each p In story.Paragraphs
If StrComp(p.Style, "Heading 1", vbTextCompare) = 0 Then
counter = counter + 1
End If
Next p
Next story
Debug.Print counter
End Sub
它适用于段落样式。对于字符样式,您应该使用 .find 方法。
我正在尝试在 VBA 中编写代码,我将在其中获取具有特定样式的段落的编号(假设为标题 1)。 我正在经历一个循环,不幸的是我收到这样的错误:
"Object variable or With block variable not set"
这是我的代码:
Public Function FindParagraph(ByVal pStyle As String) As Integer
Dim doc As Document
Dim pNum As Integer
Set doc = ActiveDocument
For pNum = 1 To doc.Paragraphs.Count
Debug.Print pNum, doc.Paragraphs(pNum).Range.Style
If doc.Paragraphs(pNum).Range.Style = pStyle Then
FindParagraph = pNum
Exit For
End If
Next pNum
End Function
Sub DoSth()
Dim i As Integer
i = FindParagraph("Heading 1")
Debug.Print i
End Sub
调试器显示问题出在这一行:
pStyle = doc.Paragraphs(i).Range.Style
实际上,我正在查看我的 Word 文档,它是 Table 目录的第一行。
你知道为什么会这样吗?
您提供的代码无法编译。它在
处给出错误While Not (IsEmpty(pStyle))
因为方法 IsEmpty 只能用于 Variant 类型,而您分配给 pStyle 的类型是字符串。为了实现您的意图,您需要将此行更改为
While Not pStyle = vbNullString
已更新以提供修改后的功能
Sub TestFindParagraph()
Dim IsFound As Boolean
IsFound = FindParagraph(ActiveDocument.StoryRanges(wdMainTextStory), "Heading 1")
End Sub
Public Function FindParagraph(ByVal SearchRange As Word.Range, ByVal ParaStyle As String) As Long
Dim ParaIndex As Long
For ParaIndex = 1 To SearchRange.Paragraphs.Count
If doc.Paragraphs(ParaIndex).Range.Style = ParaStyle Then
FindParagraph = ParaIndex
Exit Function
End If
Next
End Function
更新 2020-Apr-15 解决目录问题
当段落是 Table 的内容字段时,OP 和我自己发布的代码都失败了。发生此故障是因为 VBA 个对象的默认成员功能。
对象的默认成员是在没有限定方法的情况下给定对象实例时调用的方法。此功能有助于简化代码,但可能会导致类似于我们遇到的奇怪错误。
在 Word 中,样式是具有大量属性和方法的对象。 Style 的默认方法是 NameLocal(请参阅 Word.Style 的对象浏览器),其中 return 是一个包含字符串的 Variant(请参阅本地 windows 中样式的类型)。 因此,即使 pStyle 被定义为 String 类型,VBA 强制转换也允许将 variant/string 分配给 String pStyle,并且一切似乎都正常。
但是,对于 TOC 字段,Word 似乎没有 return 包装 TOC 字段的样式,而是 returns 'nothing' 的值,即不是目录的样式。不能将任何内容(与 vbNullString 不同)分配给字符串,因此会发生错误。
上面遇到的问题似乎有两种解决方案。
更改代码以对我们需要的信息使用正确的语法。即 Style.NameLocal。不幸的是,这个解决方案也会失败,因为我们不能在什么都不是的对象上调用方法 (NameLocal)。
将 pStyle 的变量类型从 String 更改为 Variant。变体类型可以保存对象,因此可以保存当段落是 TOC 字段时生成的任何值。
解决方案 2 似乎工作正常,但从纯粹主义者的角度来看,您将有一个中间变体变量,它捕获 Style 的结果,然后在将字符串值或 vbNullString 分配给 pStyle 之前测试该变体。
最终更新 2020 年 4 月 15 日
不幸的是,我因为一些紧急工作被 F 夫人叫走了,所以忘记添加最后的内容。
可以轻松避免默认成员陷阱。这要归功于 Rubberduck 的人们所做的出色工作。
VBA 的 Rubberduck 插件(免费)作为其众多才能之一,对 VBA 进行了更严格的代码分析(代码检查)。其中一项检查是在代码中使用默认成员的任何地方发出警告。 Rubberduck 确实消除了编写 VBA 代码时的大量痛苦,因为它可以帮助您准确理解您在代码中所做的假设(您没有意识到自己所做的假设)...
Word中的段落不可能没有段落样式,所以测试一个段落是否完全没有任何样式是没有意义的。
此外,循环遍历所有段落的效率远低于使用“查找”。例如,以下代码检索每个标题 1 的段落索引#:
Sub Demo()
Application.ScreenUpdating = False
Dim Rng As Range, i As Long
With ActiveDocument.Range
Set Rng = .Duplicate
With .Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = ""
.Replacement.Text = ""
.Style = wdStyleHeading1
.Format = True
.Forward = True
.Wrap = wdFindStop
.MatchWildcards = False
.Execute
End With
Do While .Find.Found
i = i + 1
Rng.End = .Duplicate.End
MsgBox Rng.Paragraphs.Count
'The next If ... End If block is only needed if the Found content might be in a table
If .Information(wdWithInTable) = True Then
If .End = .Cells(1).Range.End - 1 Then
.End = .Cells(1).Range.End
.Collapse wdCollapseEnd
If .Information(wdAtEndOfRowMarker) = True Then
.End = .End + 1
End If
End If
End If
'The next line is only needed if the Found content might include the document's final paragraph break
If .End = ActiveDocument.Range.End Then Exit Do
.Collapse wdCollapseEnd
.Find.Execute
Loop
End With
Application.ScreenUpdating = True
MsgBox i & " instances found."
End Sub
更多代码,但速度更快。
而是for i=1 to ....Count
尝试 for each
循环以获得更好的效率
Private Sub StylesCount()
Dim p As Paragraph
Dim story As Range
Dim counter As Long
For Each story In ActiveDocument.StoryRanges
For Each p In story.Paragraphs
If StrComp(p.Style, "Heading 1", vbTextCompare) = 0 Then
counter = counter + 1
End If
Next p
Next story
Debug.Print counter
End Sub
它适用于段落样式。对于字符样式,您应该使用 .find 方法。