使用VBA在Excel中查找上次使用的单元格时出错

时间:2012-06-23 12:20:52

标签: excel vba excel-formula

当我想找到最后使用的单元格值时,我使用:

Dim LastRow As Long

LastRow = Range("E4:E48").End(xlDown).Row

Debug.Print LastRow

当我将单个元素放入单元格时,输出错误。但是当我在单元格中放入多个值时,输出是正确的。 这背后的原因是什么?

15 个答案:

答案 0 :(得分:268)

注意:我打算将其作为“一站式帖子”,您可以使用Correct方式查找最后一行。这也将涵盖查找最后一行时要遵循的最佳做法。因此,每当遇到新的场景/信息时,我都会继续更新它。


查找最后一行的不可靠方法

查找最后一行的一些最常见的方法是非常不可靠的,因此永远不应该使用。

  1. UsedRange
  2. xlDown
  3. COUNTA
  4. UsedRange从不用于查找包含数据的最后一个单元格。这是非常不可靠的。试试这个实验。

    在单元格A5中输入内容。现在,当您使用下面给出的任何方法计算最后一行时,它将为您提供5.现在为单元格A10着色为红色。如果您现在使用以下任何代码,您仍然会得到5.如果您使用Usedrange.Rows.Count,您会得到什么?它不会是5。

    以下是展示UsedRange如何运作的方案。

    enter image description here

    xlDown同样不可靠。

    考虑这段代码

    lastrow = Range("A1").End(xlDown).Row
    

    如果只有一个单元格(A1)有数据,会发生什么?您将最终到达工作表中的最后一行!这就像选择单元格A1然后按结束键然后按向下箭头键。如果范围内有空白单元格,这也会给您带来不可靠的结果。

    CountA也不可靠,因为如果中间有空白单元格会导致错误的结果。

    因此,应该避免使用UsedRangexlDownCountA来查找最后一个单元格。


    查找列中的最后一行

    要查找Col E中的最后一行,请使用此

    With Sheets("Sheet1")
        LastRow = .Range("E" & .Rows.Count).End(xlUp).Row
    End With
    

    如果您发现.之前我们有Rows.Count。我们经常选择忽略它。有关可能出现的错误,请参阅THIS问题。我总是建议在.Rows.Count之前使用Columns.Count。该问题是代码失败的典型场景,因为{2003}及更早版本的Rows.Count会返回65536,而Excel 2007及更高版本会返回1048576。同样,Columns.Count分别返回25616384

    Excel 2007+有1048576行的上述事实也强调了这样一个事实,即我们应该始终声明将行值保持为Long而不是Integer的变量,否则你将收到Overflow错误。


    查找工作表中的最后一行

    要查找工作表中的Effective最后一行,请使用此选项。请注意Application.WorksheetFunction.CountA(.Cells)的使用。这是必需的,因为如果工作表中没有包含数据的单元格,那么.Find将为您提供Run Time Error 91: Object Variable or With block variable not set

    With Sheets("Sheet1")
        If Application.WorksheetFunction.CountA(.Cells) <> 0 Then
            lastrow = .Cells.Find(What:="*", _
                          After:=.Range("A1"), _
                          Lookat:=xlPart, _
                          LookIn:=xlFormulas, _
                          SearchOrder:=xlByRows, _
                          SearchDirection:=xlPrevious, _
                          MatchCase:=False).Row
        Else
            lastrow = 1
        End If
    End With
    

    查找表中的最后一行(ListObject)

    同样的原则适用于例如获取表格第三列中的最后一行:

    Sub FindLastRowInExcelTableColAandB()
    Dim lastRow As Long
    Dim ws As Worksheet, tbl as ListObject
    Set ws = Sheets("Sheet1")  'Modify as needed
    'Assuming the name of the table is "Table1", modify as needed
    Set tbl = ws.ListObjects("Table1")
    
    With tbl.ListColumns(3).Range
        lastrow = .Find(What:="*", _
                    After:=.Cells(1), _
                    Lookat:=xlPart, _
                    LookIn:=xlFormulas, _
                    SearchOrder:=xlByRows, _
                    SearchDirection:=xlPrevious, _
                    MatchCase:=False).Row
    End With
    
    End Sub
    

答案 1 :(得分:28)

关于找到最后一个使用过的单元格的正确方法,首先要确定使用的,然后选择合适的方法。我设想至少有三个含义:

  1. 已使用=非空白,即数据

  2. 已使用=&#34; ...正在使用中,表示包含数据或格式的部分。&#34; As per official documentation,这是Excel在保存时使用的标准。另见this。 如果没有意识到这一点,则该标准可能产生意外的结果,但也可能有意地利用(不太经常,肯定),例如突出或打印特定区域,这些区域最终可能没有数据。 当然,作为保存工作簿时使用范围的标准是可取的,以免丢失部分工作。

  3. 已使用=&#34; ...正在使用中,表示包含数据或格式&#34;的部分或条件格式。 与2.相同,但也包括作为任何条件格式规则的目标的单元格。

  4. 如何查找上次使用的单元格取决于想要的内容(您的标准)

    对于标准1,我建议您阅读this answer 。 请注意,UsedRange被认为是不可靠的。我认为这是误导性的(即&#34;不公平&#34;到UsedRange),因为UsedRange根本不是要报告包含数据的最后一个单元格。所以在这种情况下不应该使用它,如答案所示。另见this comment

    对于标准2,UsedRange是最可靠的选项,与为此用途设计的其他选项相比。它甚至不必保存工作簿以确保更新最后一个单元格。 Ctrl + End 将在保存之前转到错误的单元格 (“保存工作表之前不会重置最后一个单元格”,来自 http://msdn.microsoft.com/en-us/library/aa139976%28v=office.10%29.aspx。 这是一个旧的参考,但在这方面是有效的。)

    对于标准3,我不知道任何内置方法。 标准2不考虑条件格式。可能有基于公式的格式化单元格,UsedRange Ctrl + End 未检测到这些公式。 在图中,最后一个单元格是B3,因为格式化是明确应用于它的。单元格B6:D7具有从条件格式规则派生的格式,即使UsedRange也未检测到。 考虑到这一点需要一些VBA编程。

    enter image description here


    关于您的具体问题背后的原因是什么?

    您的代码使用您的E4系列中的第一个单元格:E48作为蹦床,使用End(xlDown) 跳跃

    &#34;错误&#34;如果您的范围中没有非空白单元格,则输出将获得,而不是第一个。然后,你在黑暗中跳跃,即在工作表下面 (你应该注意空白空字符串之间的区别!)。

    请注意:

    1. 如果您的范围包含非连续的非空白单元格,那么它也会产生错误的结果。

    2. 如果只有一个非空白单元格,但它不是第一个单元格,那么您的代码仍会提供正确的结果。

答案 2 :(得分:16)

我为确定最后一行,列和单元创建了这个一站式函数,无论是数据,格式化(分组/评论/隐藏)单元格还是条件格式

Sub LastCellMsg()
    Dim strResult As String
    Dim lngDataRow As Long
    Dim lngDataCol As Long
    Dim strDataCell As String
    Dim strDataFormatRow As String
    Dim lngDataFormatCol As Long
    Dim strDataFormatCell As String
    Dim oFormatCond As FormatCondition
    Dim lngTempRow As Long
    Dim lngTempCol As Long
    Dim lngCFRow As Long
    Dim lngCFCol As Long
    Dim strCFCell As String
    Dim lngOverallRow As Long
    Dim lngOverallCol As Long
    Dim strOverallCell As String

    With ActiveSheet

        If .ListObjects.Count > 0 Then
            MsgBox "Cannot return reliable results, as there is at least one table in the worksheet."
            Exit Sub
        End If

        strResult = "Workbook name: " & .Parent.Name & vbCrLf
        strResult = strResult & "Sheet name: " & .Name & vbCrLf

        'DATA:
        'last data row
        If Application.WorksheetFunction.CountA(.Cells) <> 0 Then
            lngDataRow = .Cells.Find(What:="*", _
             After:=.Range("A1"), _
             Lookat:=xlPart, _
             LookIn:=xlFormulas, _
             SearchOrder:=xlByRows, _
             SearchDirection:=xlPrevious, _
             MatchCase:=False).Row
        Else
            lngDataRow = 1
        End If
        'strResult = strResult & "Last data row: " & lngDataRow & vbCrLf

        'last data column
        If Application.WorksheetFunction.CountA(.Cells) <> 0 Then
            lngDataCol = .Cells.Find(What:="*", _
             After:=.Range("A1"), _
             Lookat:=xlPart, _
             LookIn:=xlFormulas, _
             SearchOrder:=xlByColumns, _
             SearchDirection:=xlPrevious, _
             MatchCase:=False).Column
        Else
            lngDataCol = 1
        End If
        'strResult = strResult & "Last data column: " & lngDataCol & vbCrLf

        'last data cell
        strDataCell = Replace(Cells(lngDataRow, lngDataCol).Address, "$", vbNullString)
        strResult = strResult & "Last data cell: " & strDataCell & vbCrLf

        'FORMATS:
        'last data/formatted/grouped/commented/hidden row
        strDataFormatRow = StrReverse(Split(StrReverse(.UsedRange.Address), "$")(0))
        'strResult = strResult & "Last data/formatted row: " & strDataFormatRow & vbCrLf

        'last data/formatted/grouped/commented/hidden column
        lngDataFormatCol = Range(StrReverse(Split(StrReverse(.UsedRange.Address), "$")(1)) & "1").Column
        'strResult = strResult & "Last data/formatted column: " & lngDataFormatCol & vbCrLf

        'last data/formatted/grouped/commented/hidden cell
        strDataFormatCell = Replace(Cells(strDataFormatRow, lngDataFormatCol).Address, "$", vbNullString)
        strResult = strResult & "Last data/formatted cell: " & strDataFormatCell & vbCrLf

        'CONDITIONAL FORMATS:
        For Each oFormatCond In .Cells.FormatConditions

            'last conditionally-formatted row
            lngTempRow = CLng(StrReverse(Split(StrReverse(oFormatCond.AppliesTo.Address), "$")(0)))
            If lngTempRow > lngCFRow Then lngCFRow = lngTempRow

            'last conditionally-formatted column
            lngTempCol = Range(StrReverse(Split(StrReverse(oFormatCond.AppliesTo.Address), "$")(1)) & "1").Column
            If lngTempCol > lngCFCol Then lngCFCol = lngTempCol
        Next
        'no results are returned for Conditional Format if there is no such
        If lngCFRow <> 0 Then
            'strResult = strResult & "Last cond-formatted row: " & lngCFRow & vbCrLf
            'strResult = strResult & "Last cond-formatted column: " & lngCFCol & vbCrLf

            'last conditionally-formatted cell
            strCFCell = Replace(Cells(lngCFRow, lngCFCol).Address, "$", vbNullString)
            strResult = strResult & "Last cond-formatted cell: " & strCFCell & vbCrLf
        End If

        'OVERALL:
        lngOverallRow = Application.WorksheetFunction.Max(lngDataRow, strDataFormatRow, lngCFRow)
        'strResult = strResult & "Last overall row: " & lngOverallRow & vbCrLf
        lngOverallCol = Application.WorksheetFunction.Max(lngDataCol, lngDataFormatCol, lngCFCol)
        'strResult = strResult & "Last overall column: " & lngOverallCol & vbCrLf
        strOverallCell = Replace(.Cells(lngOverallRow, lngOverallCol).Address, "$", vbNullString)
        strResult = strResult & "Last overall cell: " & strOverallCell & vbCrLf

        MsgBox strResult
        Debug.Print strResult

    End With

End Sub

结果如下:
determine last cell

有关更详细的结果,代码中的某些行可以取消注释:
last column, row

存在一个限制 - 如果表格中有表格,结果会变得不可靠,所以我决定在这种情况下避免运行代码:

If .ListObjects.Count > 0 Then
    MsgBox "Cannot return reliable results, as there is at least one table in the worksheet."
    Exit Sub
End If

答案 3 :(得分:9)

使用解决方案时要记住的一个重要注意事项......

LastRow = ws.Cells.Find(What:="*", After:=ws.range("a1"), SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row

...是为了确保您的LastRow变量属于Long类型:

Dim LastRow as Long

否则,在.XLSX工作簿中的某些情况下,您最终会收到OVERFLOW错误

这是我的封装函数,我将其用于各种代码用途。

Private Function FindLastRow(ws As Worksheet) As Long
    ' --------------------------------------------------------------------------------
    ' Find the last used Row on a Worksheet
    ' --------------------------------------------------------------------------------
    If WorksheetFunction.CountA(ws.Cells) > 0 Then
        ' Search for any entry, by searching backwards by Rows.
        FindLastRow = ws.Cells.Find(What:="*", After:=ws.range("a1"), SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
    End If
End Function

答案 4 :(得分:8)

我想添加Siddarth Rout给出的答案,可以通过让Find返回一个Range对象而不是行号来跳过CountA调用,然后测试返回的Range对象以查看它是否为Nothing(空白工作表)。

另外,我的任何LastRow过程的版本都会为空白工作表返回零,然后我就知道它是空白的。

答案 5 :(得分:8)

我想知道没有人提到这一点,但获取最后一个使用过的单元格的最简单方法是:

Function GetLastCell(sh as Worksheet) As Range
    GetLastCell = sh.Cells(1,1).SpecialCells(xlLastCell)
End Function

选择单元格 A1 后,这基本上会返回与 Ctrl + 结束相同的单元格。

提醒:Excel会跟踪工作表中使用过的最右下角的单元格。因此,例如,如果您在 B3 中输入内容,在 H8 中输入其他内容,然后再删除 H8 的内容,请按 Ctrl + 结束仍会将您带到 H8 单元格。上述功能具有相同的行为。

答案 6 :(得分:6)

由于最初的问题是关于找到最后一个单元格的问题在这个答案中我会列出各种可以获得意外结果的方法;请参阅my answer to "How can I find last row that contains data in the Excel sheet with a macro?"了解解决这个问题。

我首先要扩展the answer by sancho.sthe comment by GlennFromIowa,添加更多细节:

  

[...]首先要决定使用什么。我看到至少6个含义。细胞有:

     
      
  • 1)数据,即公式,可能导致空白值;
  •   
  • 2)一个值,即非空白公式或常数;
  •   
  • 3)格式化;
  •   
  • 4)条件格式化;
  •   
  • 5)与单元重叠的形状(包括注释);
  •   
  • 6)参与表(列表对象)。
  •   
     

您想要测试哪种组合?某些(例如表格)可能更难以测试,有些可能很少(例如数据范围之外的形状),但其他可能会因情况而异(例如,具有空白值的公式)。

您可能需要考虑的其他事项:

  • A)是否可以有隐藏行(例如自动过滤器),空白单元格或空白行?
  • B)什么样的表现是可以接受的?
  • C)VBA宏可以以任何方式影响工作簿或应用程序设置吗?

考虑到这一点,让我们看看获取最后一个单元的常用方法&#34;会产生意想不到的结果:

  • 问题中的.End(xlDown)代码最容易破解(例如单个非空单元格空白单元格the answer by Siddharth Rout中解释的原因(搜索&#34; xlDown同样不可靠。&#34;
  • 基于CountCountACells*.Count)或.CurrentRegion的任何解决方案也会因空白单元格或行存在而中断
  • 从列末尾向后搜索.End(xlUp)的解决方案,就像CTRL + UP一样,查找数据(生成空白值的公式被视为&#34; 可见行中的数据&#34;)(因此在启用自动过滤时使用它可能会产生不正确的结果⚠️)。

    你必须小心避免标准陷阱(有关详细信息,我将在此处再次引用the answer by Siddharth Rout,查找&#34;在列中查找最后一行&#34; 部分),例如对最后一行(Range("A65536").End(xlUp))进行硬编码,而不是依赖于sht.Rows.Count

  • .SpecialCells(xlLastCell)相当于CTRL + END,返回&#34;使用范围&#34;的最底部和最右边的单元格,因此适用于依赖&#34;的所有警告都是如此。使用范围&#34;,也适用于此方法。此外,&#34;使用范围&#34;只有在保存工作簿和访问worksheet.UsedRange时才会重置,因此xlLastCell可能会产生陈旧的结果⚠带有未保存的修改(例如,在删除某些行之后)。请参阅nearby answer by dotNET
  • sht.UsedRange(此处the answer by sancho.s详细介绍)认为数据和格式(虽然不是条件格式),重置&#34;使用的范围& #34;工作表,可能是你想要的也可能不是。

    注意一个常见的错误️是使用.UsedRange.Rows.Count⚠️,它返回行数在使用的范围内,而不是最后一行编号(如果前几行为空,则它们将不同),有关详细信息,请参阅newguy's answer to How can I find last row that contains data in the Excel sheet with a macro?

  • .Find允许您查找包含任何数据(包括公式)的最后一行或任何列中的非空值 。您可以选择是否对公式或值感兴趣,但问题是它重置了Excel的查找对话框️️⚠️中的默认值,这可能会让人非常困惑。你的用户。它也需要仔细使用,请参阅the answer by Siddharth Rout此处(&#34;在工作表中查找最后一行&#34;
  • 检查个人Cells&#39;在循环中通常比重新使用Excel函数慢(尽管仍然可以执行),但是让您准确指定要查找的内容。请参阅my solution基于UsedRange和VBA数组,以查找给定列中包含数据的最后一个单元格 - 它处理隐藏的行,过滤器,空白,不修改查找默认值,并且性能非常高。< / LI>

无论你选择什么解决方案,都要小心

  • 使用Long代替Integer来存储行号(以避免Overflow行超过65k)和
  • 始终指定您正在使用的工作表(即Dim ws As Worksheet ... ws.Range(...)而不是Range(...)
  • 使用.ValueVariant时)避免使用.Value <> ""之类的隐式转换,因为如果单元格包含错误值,它们将失败。

答案 7 :(得分:5)

sub last_filled_cell()
msgbox range("a65536").end(xlup).row
end sub

“这里a65536是列中的最后一个单元格,此代码在excel sti72003上测试”200

如果你正在使用它 “a1,048,576”

我的代码只是让初学者理解end(xlup)和其他相关命令可以做什么的概念

答案 8 :(得分:3)

然而,这个问题是在寻找使用VBA的最后一行,我认为最好包含一个工作表函数的数组公式,因为它经常被访问:

{=ADDRESS(MATCH(INDEX(D:D,MAX(IF(D:D<>"",ROW(D:D)-ROW(D1)+1)),1),D:D,0),COLUMN(D:D))}

您需要输入不带括号的公式,然后按 Shift + Ctrl + 输入以使其成为数组公式。

这将为您提供D列中最后使用过的单元格的地址。

答案 9 :(得分:2)

我一直在寻找一种模仿 CTRL + Shift + End 的方法,所以dotNET解决方案很棒,除了我的Excel 2010如果我想避免错误,我需要添加set

Function GetLastCell(sh As Worksheet) As Range
  Set GetLastCell = sh.Cells(1, 1).SpecialCells(xlLastCell)
End Function

以及如何自己检查:

Sub test()
  Dim ws As Worksheet, r As Range
  Set ws = ActiveWorkbook.Sheets("Sheet1")
  Set r = GetLastCell(ws)
  MsgBox r.Column & "-" & r.Row
End Sub

答案 10 :(得分:1)

Sub lastRow()

    Dim i As Long
        i = Cells(Rows.Count, 1).End(xlUp).Row
            MsgBox i

End Sub

sub LastRow()

'Paste & for better understanding of the working use F8 Key to run the code .

dim WS as worksheet
dim i as long

set ws = thisworkbook("SheetName")

ws.activate

ws.range("a1").select

ws.range("a1048576").select

activecell.end(xlup).select

i= activecell.row

msgbox "My Last Row Is " & i

End sub

答案 11 :(得分:1)

在过去3年多的时间里,这些是我用于查找每个已定义列(行)和行(列)的最后一行和最后一列的函数:

最后一栏:

Function lastCol(Optional wsName As String, Optional rowToCheck As Long = 1) As Long

    Dim ws  As Worksheet

    If wsName = vbNullString Then
        Set ws = ActiveSheet
    Else
        Set ws = Worksheets(wsName)
    End If

    lastCol = ws.Cells(rowToCheck, ws.Columns.Count).End(xlToLeft).Column

End Function

最后一行:

Function lastRow(Optional wsName As String, Optional columnToCheck As Long = 1) As Long

    Dim ws As Worksheet

    If wsName = vbNullString Then
        Set ws = ActiveSheet
    Else
        Set ws = Worksheets(wsName)
    End If

    lastRow = ws.Cells(ws.Rows.Count, columnToCheck).End(xlUp).Row

End Function

对于OP的情况,这是获取列E中最后一行的方法:

Debug.Print lastRow(columnToCheck:=Range("E4:E48").Column)

答案 12 :(得分:1)

这是我的两分钱。

恕我直言,隐藏行被排除数据的风险太大,以至于不能xlUp被视为一站式答案。我同意这很简单,并且在大多数情况下都可以使用,但是存在冒低估最后一行而没有任何警告的风险。对于跳入Stack Overlow并寻求“确定方法”以捕获此值的某人,这可能会在某点产生 CATASTROPHIC 结果。

Find方法是完美无缺的,我将其作为一站式答案。但是,更改Find设置的缺点可能很烦人,特别是如果它是UDF的一部分。

发布的其他答案还可以,但是复杂性有点过高。因此,这是我尝试在可靠性,最小复杂度和不使用Find之间寻求平衡。

Function LastRowNumber(Optional rng As Range) As Long

If rng Is Nothing Then
    Set rng = ActiveSheet.UsedRange
Else
    Set rng = Intersect(rng.Parent.UsedRange, rng.EntireColumn)
    If rng Is Nothing Then
        LastRowNumber = 1
        Exit Function
    ElseIf isE = 0 Then
        LastRowNumber = 1
        Exit Function

    End If

End If

LastRowNumber = rng.Cells(rng.Rows.Count, 1).Row

Do While IsEmpty(Intersect(rng, _
    rng.Parent.Rows(LastRowNumber)))

    LastRowNumber = LastRowNumber - 1
Loop

End Function

为什么这样好:

  • 非常简单,没有很多变量。
  • 允许多列。
  • 不修改Find设置
  • 如果用作UDF,并且选中了整个列,则为动态。

为什么这样不好:

  • 如果数据量非常大,并且使用范围和指定列中的最后一行之间存在巨大差距,则执行速度会变慢,在极少数情况下会显着变慢。

但是,我认为一站式解决方案具有使find设置混乱或执行速度较慢的缺点,是更好的整体解决方案。然后,用户可以知道自己的代码发生了什么,便修改自己的设置以尝试改进。使用xLUp不会警告潜在的风险,他们可以继续为知道不知道自己的代码不能正常工作多久的人提供服务。

答案 13 :(得分:0)

按范围查找列或表列(ListObject)中的最后一行

找到最后一行需要:

  1. 指定几个参数(表名、表内相对于第一列的列、工作表、范围)。
  2. 可能需要在方法之间切换。例如范围是否为表(列表对象)。使用错误的搜索类型将返回错误的结果。

这个提议的解决方案更通用,只需要范围,拼写错误的机会更少,而且很短(只需调用 MyLastRow 函数)。

<块引用>
Sub test()
Dim rng As Range
Dim Result As Long
Set rng = Worksheets(1).Range("D4")
Result = MyLastRow(rng)
End Sub
    Function MyLastRow(FirstRow As Range) As Long
    Dim WS As Worksheet
    Dim TableName As String
    Dim ColNumber As Long
    Dim LastRow As Long
    Dim FirstColumnTable As Long
    Dim ColNumberTable As Long
    Set WS = FirstRow.Worksheet
    TableName = GetTableName(FirstRow)
    ColNumber = FirstRow.Column
    
    ''If the table (ListObject) does not start in column "A" we need to calculate the 
    ''first Column table and how many Columns from its beginning the Column is located.
    If TableName <> vbNullString Then
     FirstColumnTable = WS.ListObjects(TableName).ListColumns(1).Range.Column
     ColNumberTable = ColNumber - FirstColumnTable + 1
    End If 

    If TableName = vbNullString Then
    LastRow = WS.Cells(WS.Rows.Count, ColNumber).End(xlUp).Row
    Else
    LastRow = WS.ListObjects(TableName).ListColumns(ColNumberTable).Range.Find( _
               What:="*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
    End If
    MyLastRow = LastRow
    End Function
    
    ''Get Table Name by Cell Range
    Function GetTableName(CellRange As Range) As String
        If CellRange.ListObject Is Nothing Then
            GetTableName = vbNullString
        Else
            GetTableName = CellRange.ListObject.Name
        End If
    End Function

答案 14 :(得分:0)

常规范围或表(ListObject)中的最后一行

  1. 如果范围是常规范围或表格(列表对象),则查找最后一行需要使用不同的方法。
  2. 在表格中查找最后一行需要指定额外的参数(表格名称、列相对于表格第一列的相对位置)。

我为最后一行创建了这个通用函数,无论范围类型如何。只需给它任何单元格引用,它就会返回最后一行。 无需知道范围特征,特别是如果您的范围有时是常规范围,有时是 ListObject。 在表上使用常规范围方法可能会返回错误的结果。 当然,您可以提前计划并每次都使用正确的方法,但如果您可以使用通用功能,何必费心呢?

<块引用>
Sub RunMyLastRow()
Dim Result As Long
Result = MyLastRow(Worksheets(1).Range("A1"))
End Sub
    Function MyLastRow(RefrenceRange As Range) As Long
    Dim WS As Worksheet
    Dim TableName As String
    Dim ColNumber As Long
    Dim LastRow As Long
    Dim FirstColumnTable As Long
    Dim ColNumberTable As Long
    Set WS = RefrenceRange.Worksheet
    TableName = GetTableName(RefrenceRange)
    ColNumber = RefrenceRange.Column
    
    ''If the table (ListObject) does not start in column "A" we need to calculate the 
    ''first Column table and how many Columns from its beginning the Column is located.
    If TableName <> vbNullString Then
     FirstColumnTable = WS.ListObjects(TableName).ListColumns(1).Range.Column
     ColNumberTable = ColNumber - FirstColumnTable + 1
    End If 

    If TableName = vbNullString Then
    LastRow = WS.Cells(WS.Rows.Count, ColNumber).End(xlUp).Row
    Else
    LastRow = WS.ListObjects(TableName).ListColumns(ColNumberTable).Range.Find( _
               What:="*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
    End If
    MyLastRow = LastRow
    End Function
    
<块引用>
    ''Get Table Name by Cell Range
    Function GetTableName(RefrenceRange As Range) As String
        If RefrenceRange.ListObject Is Nothing Then
            GetTableName = vbNullString
        Else
            GetTableName = RefrenceRange.ListObject.Name
        End If
    End Function