【问题标题】:How to end this loop?如何结束这个循环?
【发布时间】:2017-07-01 01:08:20
【问题描述】:

我目前编写了一个 VBA 代码,要求用户输入字符串以及某个目录,它会搜索每个文件夹、子文件夹、工作簿和工作表,直到找到用户输入的字符串。问题我遇到的是,在找到字符串后,它会继续搜索其余的文件夹。我将在其中使用它的应用程序,只有一个正在搜索的字符串。我尝试过调试,并使用带有“c”的 if 语句来匹配 str,但它一直抛出错误。代码附在下面,感谢任何帮助。

Public WS As Worksheet
Sub SearchWKBooksSubFolders(Optional Folderpath As Variant, Optional Str As Variant)
Dim myfolder As String
Dim a As Single
Dim sht As Worksheet
Dim Lrow As Single
Dim Folders() As String
Dim Folder As Variant
ReDim Folders(0)
If IsMissing(Folderpath) Then
    Set WS = Sheets.Add
    With Application.FileDialog(msoFileDialogFolderPicker)
        .Show
        myfolder = .SelectedItems(1) & "\"
    End With
    Str = Application.InputBox(prompt:="Search string:", Title:="Search all workbooks in a folder", Type:=2)
    If Str = "" Then Exit Sub
    WS.Range("A1") = "Search string:"
    WS.Range("B1") = Str
    WS.Range("A2") = "Path:"
    WS.Range("B2") = myfolder
    WS.Range("A3") = "Folderpath"
    WS.Range("B3") = "Workbook"
    WS.Range("C3") = "Worksheet"
    WS.Range("D3") = "Cell Address"
    WS.Range("E3") = "Link"
    Folderpath = myfolder
    Value = Dir(myfolder, &H1F)
Else
    If Right(Folderpath, 2) = "\\" Then
        Exit Sub
    End If
    Value = Dir(Folderpath, &H1F)
End If
Do Until Value = ""
    If Value = "." Or Value = ".." Then
    Else
        If GetAttr(Folderpath & Value) = 16 Then
            Folders(UBound(Folders)) = Value
            ReDim Preserve Folders(UBound(Folders) + 1)
        ElseIf (Right(Value, 3) = "xls" Or Right(Value, 4) = "xlsx" Or Right(Value, 4) = "xlsm") And Left(Value, 1) <> "~" Then
            On Error Resume Next
            Dim wb As Workbook
            Set wb = Workbooks.Open(Filename:=Folderpath & Value, Password:="zzzzzzzzzzzz")
            On Error GoTo 0
            'If there is an error on Workbooks.Open, then wb Is Nothing:
            If wb Is Nothing Then
                Lrow = WS.Range("A" & Rows.Count).End(xlUp).Row + 1
                WS.Range("A" & Lrow).Value = Value
                WS.Range("B" & Lrow).Value = "Password protected"
            Else
                For Each sht In wb.Worksheets
                    'Expand all groups in sheet
                    sht.Unprotect

                    sht.Outline.ShowLevels RowLevels:=8, ColumnLevels:=8
                    Set c = sht.Cells.Find(Str, After:=sht.Cells(1, 1), LookIn:=xlValues, LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext)
                    If Not c Is Nothing Then
                        firstAddress = c.Address
                        Do
                            Lrow = WS.Range("A" & Rows.Count).End(xlUp).Row + 1
                            WS.Range("A" & Lrow).Value = Folderpath
                            WS.Range("B" & Lrow).Value = Value
                            WS.Range("C" & Lrow).Value = sht.Name
                            WS.Range("D" & Lrow).Value = c.Address
                            WS.Hyperlinks.Add Anchor:=WS.Range("E" & Lrow), Address:=Folderpath & Value, SubAddress:= _
                            "'" & sht.Name & "'" & "!" & c.Address, TextToDisplay:="Link"
                            Set c = sht.Cells.FindNext(After:=c)

                        Loop While Not c Is Nothing And c.Address <> firstAddress
                    End If
                Next sht
                wb.Close False
            End If
        End If
    End If
    Value = Dir
Loop
For Each Folder In Folders
    Call SearchWKBooksSubFolders(Folderpath & Folder & "\", Str)
Next Folder
Cells.EntireColumn.AutoFit
End Sub

【问题讨论】:

  • 我回滚了编辑以显示原始代码。通过进行代码编辑,您使答案充其量是令人困惑的,而在最坏的情况下却是无关紧要的。您最好的选择是将所有新代码发布在您自己的答案中,而不是您发布的小片段。

标签: vba excel


【解决方案1】:

添加一个您设置为True 的布尔变量,以表明您已找到所需内容。像这样的:

Sub SearchWKBooksSubFolders(Optional Folderpath As Variant, Optional Str As Variant)
  Dim myfolder As String
  Dim a       As Single
  Dim sht     As Worksheet
  Dim Lrow    As Single
  Dim Folders() As String
  Dim Folder  As Variant
  ReDim Folders(0)
  If IsMissing(Folderpath) Then
    Set WS = Sheets.Add
    With Application.FileDialog(msoFileDialogFolderPicker)
      .Show
      myfolder = .SelectedItems(1) & "\"
    End With
    Str = Application.InputBox(prompt:="Search string:", Title:="Search all workbooks in a folder", Type:=2)
    If Str = "" Then Exit Sub
    WS.Range("A1") = "Search string:"
    WS.Range("B1") = Str
    WS.Range("A2") = "Path:"
    WS.Range("B2") = myfolder
    WS.Range("A3") = "Folderpath"
    WS.Range("B3") = "Workbook"
    WS.Range("C3") = "Worksheet"
    WS.Range("D3") = "Cell Address"
    WS.Range("E3") = "Link"
    Folderpath = myfolder
    value = Dir(myfolder, &H1F)
  Else
    If Right(Folderpath, 2) = "\\" Then
      Exit Sub
    End If
    value = Dir(Folderpath, &H1F)
  End If
'---Add this:
  Dim TimeToStop As Boolean
'---Change this:
  Do Until TimeToStop
    If value = "." Or value = ".." Then
    Else
      If GetAttr(Folderpath & value) = 16 Then
        Folders(UBound(Folders)) = value
        ReDim Preserve Folders(UBound(Folders) + 1)
      ElseIf (Right(value, 3) = "xls" Or Right(value, 4) = "xlsx" Or Right(value, 4) = "xlsm") And Left(value, 1) <> "~" Then
        On Error Resume Next
        Dim wb As Workbook
        Set wb = Workbooks.Open(fileName:=Folderpath & value, Password:="zzzzzzzzzzzz")
        On Error GoTo 0
        'If there is an error on Workbooks.Open, then wb Is Nothing:
        If wb Is Nothing Then
          Lrow = WS.Range("A" & Rows.Count).End(xlUp).Row + 1
          WS.Range("A" & Lrow).value = value
          WS.Range("B" & Lrow).value = "Password protected"
        Else
          For Each sht In wb.Worksheets
            'Expand all groups in sheet
            sht.Unprotect

            sht.Outline.ShowLevels RowLevels:=8, ColumnLevels:=8
            Set c = sht.Cells.Find(Str, After:=sht.Cells(1, 1), LookIn:=xlValues, LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext)
            If Not c Is Nothing Then
'---Add this
              TimeToStop = True 'since we found what we're looking for
              firstAddress = c.Address
              Do
                Lrow = WS.Range("A" & Rows.Count).End(xlUp).Row + 1
                WS.Range("A" & Lrow).value = Folderpath
                WS.Range("B" & Lrow).value = value
                WS.Range("C" & Lrow).value = sht.Name
                WS.Range("D" & Lrow).value = c.Address
                WS.Hyperlinks.Add Anchor:=WS.Range("E" & Lrow), Address:=Folderpath & value, SubAddress:= _
                                  "'" & sht.Name & "'" & "!" & c.Address, TextToDisplay:="Link"
                Set c = sht.Cells.FindNext(After:=c)
              Loop While Not c Is Nothing And c.Address <> firstAddress
            End If
          Next sht
          wb.Close False
        End If
      End If
    End If
    value = Dir
'---Add these 3 lines
    If Len(value) = 0 Then
      TimeToStop = True
    End If
  Loop
  For Each Folder In Folders
    Call SearchWKBooksSubFolders(Folderpath & Folder & "\", Str)
  Next Folder
  Cells.EntireColumn.AutoFit
End Sub

请注意,您正在递归调用您的例程:

  For Each Folder In Folders
    Call SearchWKBooksSubFolders(Folderpath & Folder & "\", Str)
  Next Folder

一旦您完成了所有的搜索程序,您将重新开始,因为您是从您的Sub 中调用您的Sub。不知道这是否是您所追求的,这可能是进一步意外循环的另一个原因。

【讨论】:

  • 谢谢!我什至没有意识到 Excel VBA 使用了布尔值。我会试试这个,让你知道它是怎么回事。
  • 我在“Lrow = WS.Range("A" & Rows.Count).End(xlUp).Row + 1" 行收到“需要对象”错误。我将 timetostop = true 移动到该循环的末尾,因为它仍然需要记录数据路径和单元位置以及链接,然后它需要停止。但由于错误,它甚至不会现在开始记录信息。
  • @mmajdalani A) 由于TimeToStop 在循环内设置,但在循环完成处理之前未检查where 在循环中设置它并不重要。 (设置为True 的次数也无关紧要,它只测试一次) B)我不确定 which Lrow = WS.Range... 行出错,但我敢打赌是因为对Rows.Count的不合格调用引起的。这将从 当前 工作表中提取,并且不知何故,它现在可能正在查看错误的工作表。
  • 是的,发生错误的特定 Lrow 行就在“do”下面,我不确定它为什么突然抛出错误。我对 VBA 还很陌生,我仍在努力解决这个程序中的问题。这真的是我需要它做的最后一件事。但是我也尝试编写另一个 if 语句来检查“值”以及检查 c 的值,但它不会让我对 c 做任何事情,它还会不断抛出对象错误。我不确定我现在能做什么。
  • 使用调试器和Immediate Window (Ctrl-G)。 c 未声明,因此 VBA 在您第一次使用它时将其声明为 variant。这是一个对象,所以Immediate Window 中的?c 会给你带来另一个Object required 错误。但是,?c.(注意尾随的“.”)将使 IntelliSense 列出您可以检查的所有内容,以便您判断它是否分配正确。
【解决方案2】:

"如果 Str = c.Value 则转到 85"

改成

"如果 Str = c.Value 则结束"

【讨论】:

  • Exit Sub 可能更合适。请注意,End 的范围要大得多,虽然它可能适用于这种特殊情况,但在其他情况下 End 是不可取的。 stackoverflow.com/questions/36491908/… End 停止所有执行。虽然这听起来很诱人,但它也会清除所有全局和静态变量(在这种情况下没有,但在其他情况下您可能有这些)。
  • 使用GoTo 可以让你摆脱代码循环,但是这样做被认为是不好的做法,因为它通常会导致令人讨厌的、难以消除的错误,并且通常难以阅读和遵循意大利面条代码。在某些情况下它是一个可以接受的结构,但通常这是一个坏主意。
猜你喜欢
  • 2013-05-14
  • 1970-01-01
  • 1970-01-01
  • 2014-07-01
  • 1970-01-01
  • 2017-08-31
  • 2021-12-31
  • 1970-01-01
  • 2017-04-18
相关资源
最近更新 更多