【问题标题】:VBA Error Handling GoTo Next Loop instead of resumingVBA错误处理转到下一个循环而不是恢复
【发布时间】:2021-04-03 12:32:05
【问题描述】:

当导入选项卡中没有文件路径时,此代码会产生错误。因此,我包含On Error Resume Next 以便运行下一个循环。但是,在On Error Resume Next 之后,代码继续运行复制操作,这弄乱了我要复制到的选项卡。

我确定解决方案是 On Error 代码应该进入下一个循环而不是继续操作。有人对如何更改错误处理来做到这一点有任何意见吗?

Sub ImportBS()

Dim filePath As String
Dim SourceWb As Workbook
Dim TargetWb As Workbook
Dim Cell As Range
Dim i As Integer
Dim k As Integer
Dim Lastrow As Long


'SourceWb - Workbook were data is copied from
'TargetWb - Workbook were data is copied to and links are stored

Application.ScreenUpdating = False

Set TargetWb = Application.Workbooks("APC Refi Tracker.xlsb")
Lastrow = TargetWb.Sheets("Import").Range("F100").End(xlUp).Row - 6


    For k = 1 To Lastrow
    

        filePath = TargetWb.Sheets("Import").Range("F" & 6 + k).Value
        Set SourceWb = Workbooks.Open(filePath)
    
    On Error Resume Next
        Range("A1").CurrentRegion.Copy
        TargetWb.Sheets("Balance Sheet Drop").Range("D" & 2 + (k - 1) * 149).PasteSpecial Paste:=xlPasteValues
        Range("A1").Copy
        Application.CutCopyMode = False
        SourceWb.Close

    Next

Application.ScreenUpdating = True

Worksheets("Import").Activate

    MsgBox "All done!"

End Sub

【问题讨论】:

  • 您假设APC Refi Tracker.xlsb 已经打开。您的代码是位于其中还是位于第三个工作簿中?您正在使用F100。第 100 行以下有数据吗?如果这个Range("A1").CurrentRegion.Copy 指的是刚刚打开的Source Workbook 中的ActiveSheet,它的名称或索引是什么?
  • 在 VBA 中,如果您正在寻找一种允许您从 for 循环中的“下一个”语句“继续”的结构,您通常会考虑将代码移动到单独的函数中并使用保护该函数中的语句反转了强制尝试继续的测试的逻辑。
  • @VBasic2008 APC Refi Tracker.xlsb 是代码的宿主,在运行时是打开的。导入选项卡位于 Refi Tracker 中并托管链接,但通常情况下我没有此特定交易的链接,因此目的是跳过该字段。由于这会在代码中产生错误,因此我在 Error Resume Next 中添加了代码,这可能不是一种很好的错误处理方式。
  • 这使得源工作表名称或索引(在我的解决方案中称为srcID)成为唯一未澄清的“变量”。这很重要,因为如果您保存一个或多个源工作簿而另一个不是预期的活动工作表,您的代码可能(将)失败。我指的是Range("A1").CurrentRegion.Copy 这行应该类似于SourceWb.Worksheets("Sheet1").Range("A1").CurrentRegion.Copy

标签: excel vba error-handling


【解决方案1】:

我会以不同的方式做这件事。我会使用Dir function 检查路径,然后决定要做什么。这是一个例子

For k = 1 To Lastrow
    filePath = TargetWb.Sheets("Import").Range("F" & 6 + k).Value
    
    '~~> Check if the path is valid
    If Not Dir(filePath, vbNormal) = vbNullString Then
        Set SourceWb = Workbooks.Open(filePath)
        
        '
        '~~> Rest of your code
        '
    End If
Next

【讨论】:

  • 有道理。但是你有没有机会知道为什么它是这样实现的?
  • 我猜对于我们不知道字符串是什么的场景。文件还是目录?例如,当我们得到一个字符串(比如来自数据库)并且我们需要检查它是否有效时?
  • 我认为你不应该避免 On Error 例如如果filePath 是视频文件会怎样?当然,您可以创建一个允许的扩展数组以与Dir 或某些字符串函数一起使用,或者使用FileSystemObject 或其他什么,但这会使事情复杂化。 freeflow 的评论可能表明要创建一个类似isExcelFile 的函数。
  • @VBasic2008 哦,当然。你应该总是像我展示的那样做一个正确的处理HERE 所以是的,我会做正确的处理,我仍然会按照我上面展示的方式做:) 但是我相信每个人都有自己的偏好。跨度>
【解决方案2】:

导入数据

快速修复

For k = 1 To Lastrow
    filePath = TargetWb.Sheets("Import").Range("F" & 6 + k).Value
    Set SourceWb = Nothing
    On Error Resume Next
    Set SourceWb = Workbooks.Open(filePath)
    On Error GoTo 0
    If Not SourceWb Is Nothing Then
        Range("A1").CurrentRegion.Copy
        TargetWb.Sheets("Balance Sheet Drop").Range("D" & 2 + (k - 1) * 149).PasteSpecial Paste:=xlPasteValues
        Range("A1").Copy
        Application.CutCopyMode = False
        SourceWb.Close
    'Else ' File not found.
    End If
Next

改进

  • 未测试
  • 在使用代码之前调整(检查)常量部分中的值。

Option Explicit

Sub ImportBS()

    ' Destination Read
    Const rName As String = "Import" ' where file paths are stored.
    Const rFirstRow As Long = 7
    Const rCol As Variant = "F" ' or 6
    ' Destination Write
    Const wName As String = "Balance Sheet Drop" ' where data is copied to.
    Const wFirstCell As String = "D2"
    Const wRowOffset As Long = 149
    ' Source
    Const srcID As Variant = "Sheet1" ' or e.g. 1 ' where data is copied from.
    Const srcFirstCell As String = "A1"
    
    ' Define Destination Worksheets.
    ' Note that if the workbook "APC Refi Tracker.xlsb" contains this
    ' code, you should use 'Set dstWB = ThisWorkbook' instead which would
    ' make the code more readable, but would also allow you to change
    ' the workbook's name and the code would still work.
    Dim dstWB As Workbook: Set dstWB = Workbooks("APC Refi Tracker.xlsb")
    Dim wsR As Worksheet: Set wsR = dstWB.Worksheets(rName)
    Dim wsW As Worksheet: Set wsW = dstWB.Worksheets(wName)
    
    ' Define Last Row in Destination Read Worksheet.
    Dim rLastRow As Long
    With dstWB.Worksheets(rName)
        rLastRow = .Cells(.Rows.Count, rCol).End(xlUp).Row
    End With
    
    ' Declare additional variables to use in the upcoming loop.
    Dim srcFilePath As String  ' Source File Path
    Dim srcWB As Workbook      ' Source Workbook
    Dim rng As Range           ' Source Range
    Dim i As Long              ' Destination Read Worksheet Rows Counter
    Dim k As Long              ' Destination Write Worksheet Write Counter
    
    Application.ScreenUpdating = False
    
    ' Loop through rows of Destination Read Worksheet
    ' (or loop through Source Workbooks).
    For i = rFirstRow To rLastRow
        ' Read Current Source File Path from Destination Read Worksheet.
        srcFilePath = wsR.Cells(i, rCol).Value
        ' Attempt to open Current Source Workbook.
        Set srcWB = Nothing
        On Error Resume Next
        Set srcWB = Workbooks.Open(srcFilePath)
        On Error GoTo 0
        ' If Current Source Workbook was opened...
        If Not srcWB Is Nothing Then
            ' Define Source Range.
            Set rng = srcWB.Worksheets(srcID).Range(srcFirstCell).CurrentRegion
            ' Define Destination First Cell Range.
            k = k + 1
            ' If a worksheet could not be opened and you want to skip
            ' the 149 lines then replace 'k - 1' with 'i - rFirstRow'
            ' in the following line.
            With wsW.Range(wFirstCell).Offset((k - 1) * wRowOffset)
                ' Write values from Source Range to Destination Range.
                .Resize(rng.Rows.Count, rng.Columns.Count).Value = rng.Value
            End With
            ' Close Source Workbook.
            srcWB.Close SaveChanges:=False
        'Else ' Current Source Workbook was not found.
        End If
    Next
    ' Note that there has been no change of the 'Selection' in any
    ' of the worksheets i.e. what was active at the beginning is still active.
       
    ' Save Destination Workbook.
    'dstWB.Save
    
    Application.ScreenUpdating = True
    
    MsgBox "Data sets copied    : " & k & vbLf _
        & "Data sets not copied: " & i - rFirstRow - k, vbInformation, "Success"

End Sub

【讨论】:

  • 谢谢,我对此进行了测试,它会在此行Set rng = srcWB.Worksheets(srcID).Range(srcFirstCell).CurrentRegion 中产生下标超出范围的错误。你知道如何解决这个@VBasic2008 吗?源工作簿将在整个操作过程中发生变化,因为工作簿是打开和关闭的
  • 您是否将srcID 更改为Source Workbookworksheet 的实际name(或index)?您不是从Source workbook 阅读,而是从它的worksheets 之一阅读。
  • 谢谢@VBasic2008 更改为实际工作表名称后,代码现在不会产生错误并运行操作。但是,在我的情况下,出于测试目的,我在循环 1 到 3 中有链接,然后在循环 4 到 6 中没有链接,而 7 是最后一个链接。剩下的唯一问题是代码现在正在将循环 7 中打开的“srcWB”中的信息复制到需要保存循环 4 数据的“dstWB”。因此,代码缺少在循环 7 之前未打开的链接计数器,以便将其复制到正确的目的地。
  • 在代码中查找以下注释:如果无法打开工作表并且您想跳过 149 行,则将以下中的 'k - 1' 替换为 'i - rFirstRow'行。并且不要忘记将Set dstWB = Workbooks("APC Refi Tracker.xlsb")替换为Set dstWB = ThisWorkbook,原因在相关代码行之前的注释中提到。
【解决方案3】:

编辑:更正代码

试试这个代码(感谢super-symmetry 的编辑,他也链接到this post):

Sub ImportBS()
    
    Dim filePath As String
    Dim SourceWb As Workbook
    Dim TargetWb As Workbook
    Dim Cell As Range
    Dim i As Integer
    Dim k As Integer
    Dim Lastrow As Long
    
    
    'SourceWb - Workbook were data is copied from
    'TargetWb - Workbook were data is copied to and links are stored
    
    Application.ScreenUpdating = False
    
    Set TargetWb = Application.Workbooks("APC Refi Tracker.xlsb")
    Lastrow = TargetWb.Sheets("Import").Range("F100").End(xlUp).Row - 6
    
    On Error Resume Next
    
    For k = 1 To Lastrow
        
        
        filePath = TargetWb.Sheets("Import").Range("F" & 6 + k).Value
        
        Set SourceWb = Workbooks.Open(filePath)
        
        If Err <> 0 Then GoTo Error_Handler
        
        Range("A1").CurrentRegion.Copy
        TargetWb.Sheets("Balance Sheet Drop").Range("D" & 2 + (k - 1) * 149).PasteSpecial Paste:=xlPasteValues
        Range("A1").Copy
        Application.CutCopyMode = False
        SourceWb.Close
Leap:
    Next
    
    On Error GoTo -1
    
    Exit Sub
    
    Error_Handler:
    Err.Clear
    GoTo Leap
    
End Sub    

第一个(错误的)答案

如果你想在出错的情况下跳过部分代码,你可以使用这样的东西:

    For k = 1 To Lastrow
    

        filePath = TargetWb.Sheets("Import").Range("F" & 6 + k).Value
        Set SourceWb = Workbooks.Open(filePath)
    
    On Error GoTo Leap
        Range("A1").CurrentRegion.Copy
        TargetWb.Sheets("Balance Sheet Drop").Range("D" & 2 + (k - 1) * 149).PasteSpecial Paste:=xlPasteValues
        Range("A1").Copy
        Application.CutCopyMode = False
        SourceWb.Close
Leap:
    Next

我想错误应该出现在Set SourceWb = Workbooks.Open(filePath) 行。在这种情况下,您可能应该将行 On Error GoTo Leap 放在 for 的开头之前;这样,如果列表的第一个为空,它将跳转到下一个单元格。我还建议在 for 结束后放置 On Error GoTo -1。像这样:

    On Error GoTo Leap
    
    For k = 1 To Lastrow
    

        filePath = TargetWb.Sheets("Import").Range("F" & 6 + k).Value
        Set SourceWb = Workbooks.Open(filePath)
        
        Range("A1").CurrentRegion.Copy
        TargetWb.Sheets("Balance Sheet Drop").Range("D" & 2 + (k - 1) * 149).PasteSpecial Paste:=xlPasteValues
        Range("A1").Copy
        Application.CutCopyMode = False
        SourceWb.Close
Leap:
    Next
    
    On Error GoTo -1

【讨论】:

  • 您的代码将适用于第一个错误。但是,如果循环内发生另一个错误,您的代码将失败,因为您没有告诉 vba 您已经处理了第一个错误。 VBA 需要某种Resume 语句来满足已处理的错误。看看this 的帖子。
  • 你是对的。谢谢你。我会编辑答案。
  • 我认为On Error Goto -1VBA 中不可行。如果我错了,您能否分享一个文档链接,或者至少分享一下它应该做什么。
  • 我在电子表格中包含了上面的代码。但是,一旦出现错误,它仍然没有跳过复制和粘贴操作。因此我进行了修改,将If Err &lt;&gt; 0 Then GoTo Error_Handler 移动到Set SourceWb = Workbooks.Open(filePath) 下。一旦有空链接,它就会正确跳跃,但是一旦从空链接更改为打开的链接,它仍然会产生 1004 错误并且不执行复制粘贴操作。
  • @VBasic2008:我有this,它似乎适用于VBA。希望对你有帮助^^。
【解决方案4】:

试试这个:

For k = 1 To Lastrow
    filePath = TargetWb.Sheets("Import").Range("F" & 6 + k).Value
    Set SourceWb = Workbooks.Open(filePath)

    On Error Resume Next
    Range("A1").CurrentRegion.Copy
    If Err <> 0 Then GoTo ContinuationPoint
    On Error GoTo 0
    TargetWb.Sheets("Balance Sheet Drop").Range("D" & 2 + (k - 1) * 149).PasteSpecial 
    Paste:=xlPasteValues
    Range("A1").Copy
    Application.CutCopyMode = False
    SourceWb.Close

ContinuationPoint:
    On Error GoTo 0
Next

注意两点。我在那里添加了两次On Error GoTo 0。当您使用On Error Resume Next 时,您实际上已经关闭了错误处理。这现在将其重新打开。如果您在尝试复制时遇到错误,那么它将跳转到ContinuationPoint(您可以将其重命名为您想要的任何名称)。无论哪种方式,我们都会重新打开错误处理。

【讨论】:

  • 感谢@SandPiper 我测试了您的修改 - 代码现在产生 1004 错误,这是在 Set SourceWb = Workbooks.Open(filePath) 为空白时引起的。我不确定为什么会发生这种情况,因为下一行显示 @987654326 @ 所以我认为它不应该停止。
  • @Juli44 如果您指定的 filePath 实际上不存在,则会发生 1004 错误。如果您将 On Error Resume Next 移动到您的 set 语句之前,那么它将继续。它在此处以 1004 中断,因为我们稍后使用 On Error GoTo 0 命令重新打开了错误检查。这有意义吗?
猜你喜欢
  • 2015-06-06
  • 2014-02-20
  • 1970-01-01
  • 2011-11-30
  • 1970-01-01
  • 2021-09-01
  • 2020-03-09
  • 2019-01-08
  • 1970-01-01
相关资源
最近更新 更多