【问题标题】:Sometimes can't assign to array and sometimes can有时不能分配给数组,有时可以
【发布时间】:2019-05-19 17:37:59
【问题描述】:

我有下一个代码:

Function findRanges(keyword) As Variant()

Dim foundRanges(), rngSearch As Range
Dim i, foundCount As Integer

i = 0
foundCount = 0
ReDim foundRanges(0)

Set rngSearch = ActiveDocument.Range
    Do While rngSearch.Find.Execute(FindText:=keyword, MatchWholeWord:=True, Forward:=True) = True
        Set foundRanges(i) = rngSearch.Duplicate
        i = i + 1
        ReDim Preserve foundRanges(UBound(foundRanges) + 1)
        rngSearch.Collapse Direction:=wdCollapseEnd

    Loop

ReDim Preserve foundRanges(UBound(foundRanges) - 1)

findRanges = foundRanges

End Function

还有:

Sub test()
Dim rngIAM_Code() As Range
...
Dim rngIAM_Title() As Range

rngIAM_Code = findRanges("IAM_Code")
...
rngIAM_Title = findRanges("IAM_Title")


End Sub

非常令人困惑的是,有时编译器会说“无法分配给数组”,有时它工作正常。例如,当我只尝试搜索一个值并填充一个数组时,代码有效。当我尝试填充两个数组时,出现错误“无法分配给数组”。然后我可以像这样切换代码行:

rngIAM_Title = findRanges("IAM_Title")
...
rngIAM_Code = findRanges("IAM_Code")

然后错误发生在另一个数组上。错误可能发生在任何地方:在第一行、中间或最后,但只要我不移动行,它是一致的。再说一次,如果我在子“测试”中只留下一两行带有数组的代码,一切正常。

【问题讨论】:

  • 您是否随时 ReDim rngIAM_CoderngIAM_Title
  • 此外,我认为您遇到的错误是因为您将变量数组分配给数组。如果您将Dim rngIAM_Code() as range 更改为Dim rngIAM_Code as variant,我认为您应该会很好。请注意,这是一个变体数组。
  • foundRanges()i 被初始化为变体。请参阅:stackoverflow.com/a/28238732/To 正确声明它们使用 Dim i as Integer, dim foundCount As Integer,或直接使用 Dim i as Long, dim foundCount As Long,因为 VBA 无论如何都会将 Integer 存储为 Long(内部)stackoverflow.com/a/26409520
  • 另一方面,也许使用Collection 来存储您的范围,这样可以避免重新调光,您可以使用aCollection.Count 来获取计数。然后,您也可以只运行 for each aSomething in aCollection 来遍历每个存储的对象。

标签: arrays vba ms-word


【解决方案1】:

以下对我有用。

在这段代码中,每个对象变量都被显式地分配了一个类型。在 VBA 中,每个 变量都必须输入类型,否则默认分配类型为 Variant。例如,在下面的声明行中,foundRanges() 的类型为 Variant,因为它后面没有带有数据类型的 As。与问题下一行代码中的i 相同。

Dim foundRanges(), rngSearch As Range

由于调用过程中的数组是Range 类型,因此函数应该返回相同的类型。

我还冒昧地将Document 对象传递给函数,因为可以想象,有一天,有问题的文档可能不是ActiveDocument,而是使用Documents.OpenDocuments.Add 分配的Document 对象。如果不需要,可以改回来,但不依赖ActiveDocument 更可靠...

此外,我在Find.Execute 中添加了Wrap 参数 - 在循环中执行 Find 时指定它总是一个好主意,以防止在文档开头重新开始搜索 (wdFindContinue)。

Sub testRangesInArrays()
    Dim rngIAM_Code() As Range
    Dim rngIAM_Title() As Range

    rngIAM_Code = findRanges("You", ActiveDocument)
    rngIAM_Title = findRanges("change", ActiveDocument)
End Sub

Function findRanges(keyword As String, doc As Word.Document) As Range()
    Dim foundRanges() As Range, rngSearch As Range
    Dim i As Integer, foundCount As Integer

    i = 0
    foundCount = 0
    ReDim foundRanges(0)

    Set rngSearch = doc.content
    Do While rngSearch.Find.Execute(findText:=keyword, MatchWholeWord:=True, _
                       Forward:=True, wrap:=wdFindStop) = True
        Set foundRanges(i) = rngSearch.Duplicate
        ReDim Preserve foundRanges(UBound(foundRanges) + 1)
        i = i + 1
        rngSearch.Collapse Direction:=wdCollapseEnd
    Loop

    findRanges = foundRanges

End Function

【讨论】:

    【解决方案2】:

    这是一个基于集合而不是数组的替代方案:
    我还使用了包含 Cindys Input 的关于传递文档和添加换行的方法。
    我不完全知道您将返回值用于什么,但总的来说,集合比数组更灵活。
    我还删除了下划线,因为它们表示已实现接口的功能,可能会在以后引起问题。在实现Interface时使用(提高可读性)。
    正如here 解释的那样,您可以使用 wrap 或 collapse 来防止连续循环。

    Option Explicit
    
    Sub test()
        Dim rngIAMCode As Collection
        Dim rngIAMTitle As Collection
    
        Set rngIAMCode = findRanges("IAM_Code", ActiveDocument)
        Set rngIAMTitle = findRanges("IAM_Title", ActiveDocument)
    
        Debug.Print "Code found : " & rngIAMCode.Count & " Times."
        Debug.Print "Title found : " & rngIAMTitle.Count & " Times."
    
    End Sub
    
    
    
    Function findRanges(ByVal keyword As String, doc As Document) As Collection
    
        Set findRanges = New Collection
        Dim rngSearch As Range
    
        Set rngSearch = doc.Content
        With rngSearch.Find
            .Text = keyword
            .MatchWholeWord = True
            .Forward = True
            .Wrap = wdFindStop
    
            While .Execute
                findRanges.Add rngSearch.Duplicate
                rngSearch.Collapse Direction:=wdCollapseEnd
            Wend
    
        End With
    End Function
    

    【讨论】:

    • “我还删除了下划线,因为它们表示已实现接口的功能,并且可能会在以后引起问题。”不是真的,不是在VBA中。在 VBA 中,这只是一个人的约定——编程语言不在乎。
    • 谢谢!我合并了你的方法,效果很好
    • @Сергей 如果您发现多个“答案”有用,您应该能够投票(单击向上箭头)您未标记为“答案”的任何贡献(带有复选标记)。这是向其他人表明信息有用的更可靠的方式 - cmets 往往会被删除......您应该能够对网站上您认为有帮助的任何贡献(问题和答案)进行投票。一旦您获得更多声望点 (125),您还可以对您认为具有破坏性的贡献投反对票。
    • @CindyMeister 是的,只有在接口内的函数名称中使用下划线时才会出现问题。但它(IMO)提高了代码的可读性。
    猜你喜欢
    • 2011-10-11
    • 1970-01-01
    • 2014-12-14
    • 1970-01-01
    • 1970-01-01
    • 2013-04-21
    • 2018-01-28
    • 2021-10-25
    • 2013-08-06
    相关资源
    最近更新 更多