使用VBA在Excel中查找最后使用的单元格时出错


179

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

Dim LastRow As Long

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

Debug.Print LastRow

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


Answers:


309

注意:我打算将其设为“一站式发布”,您可以在其中使用Correct找到最后一行的方式。这还将涵盖查找最后一行时遵循的最佳实践。因此,每当遇到新的情况/信息时,我都会继续进行更新。


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

一些最不可靠的查找最后一行的方法,因此永远不要使用。

  1. 二手范围
  2. xlDown
  3. CountA

UsedRange是否应NEVER被用来寻找具有数据的最后一个单元格。这是非常不可靠的。试试这个实验。

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

这是一个演示如何UsedRange工作的方案。

在此处输入图片说明

xlDown 同样是不可靠的。

考虑这段代码

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

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

CountA 也是不可靠的,因为如果它们之间有空白单元格,则会给您不正确的结果。

因此应避免使用UsedRangexlDownCountA找到最后一个单元格。


查找列中的最后一行

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

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

如果您注意到我们.之前有过Rows.Count。我们经常选择忽略这一点。有关可能出现的错误,请参阅问题。我总是建议使用.before Rows.CountColumns.Count。这个问题是一个典型的场景,其中的代码将失败,因为Rows.Count回报65536为Excel 2003及更早版本1048576的Excel 2007和更高版本。同样,分别Columns.Count返回25616384

以上事实的Excel 2007+具有1048576行也强调这一事实,我们应该始终宣布将举行的行值作为变量Long,而不是Integer其他人,你会得到一个Overflow错误。

请注意,此方法将跳过任何隐藏的行。回顾上面的A列屏幕截图,如果第8行被隐藏,则此方法将返回5而不是8


在工作表中查找最后一行

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

9
@phan:在单元格A5中输入内容。现在,当您使用上述任何一种方法计算最后一行时,它将得到5。现在将单元格A10涂成红色。如果您现在使用上述任何代码,您仍然会得到5。如果您使用Usedrange.Rows.Count得到的是什么?不会是5。Usedrange非常不可靠地找到最后一行。
Siddharth Rout 2012年

6
请注意,.Find不幸地弄乱了“查找”对话框中的用户设置-即Excel仅具有该对话框的一组设置,而您使用.Find替换了它们。另一个技巧是仍然使用UsedRange,但将其用作绝对(但不可靠)最大值,从中可以确定正确的最大值。
卡尔·科林

4
@CarlColijn:我不会把它弄得一团糟。:) Excel只是remembers最后的设置。甚至当你手工做Find,它会记住最后一次设置这实际上是一个福音,如果一个人知道这个“事实”
亚洲时报Siddharth溃败

3
@KeithPark:请继续:)知识只有传播才有意义:)
Siddharth Rout 2014年

9
我认为您的描述UsedRange找到最后一个有数据的单元格非常不可靠)的描述具有误导性。UsedRange即使在某些情况下它可能会给出正确的结果,也根本不打算将其用于该目的。我认为所提出的实验加剧了混乱。使用UsedRange($ A $ 1:$ A $ 8)获得的结果不取决于首先输入数据还是将其删除。即使没有输入数据将其删除,右侧的数字也将保持不变。请看我的回答。
sancho.s ReinstateMonicaCellio 2014年

34

注意:此答案受此评论的激励。的目的与UsedRange上面答案中提到的目的不同。

关于找到最后使用的电池的正确方法,首先必须确定认为使用了什么电池,然后选择一种合适的方法。我至少想到了三种含义:

  1. 已使用=非空白,即具有data

  2. Used =“ ...正在使用中,表示包含数据或格式的部分。” 根据官方文档,这是Excel在保存时使用的标准。另请参阅此官方文档。如果没有意识到这一点,则该准则可能会产生意想不到的结果,但也可能有意地利用它(不太经常,肯定地),例如,突出显示或打印可能最终没有数据的特定区域。并且,当然,作为保存工作簿时使用范围的标准是可取的,以免丢失一部分工作。

  3. Used =“ ...正在使用中,表示包含数据或格式的部分” 或条件格式。 与2相同,但还包括任何条件格式设置规则的目标单元格。

如何找到最后使用的单元格取决于您的要求(您的标准)

对于标准1,建议阅读此答案。请注意,这UsedRange被引用为不可靠的。我认为这具有误导性(即,对而言“不公平” UsedRange),因为UsedRange这并不意味着要报告包含数据的最后一个单元格。因此,如该答案所示,不应在这种情况下使用它。另请参阅此评论

与标准2相比,标准2 UsedRange是最可靠的选择。甚至没有必要保存工作簿以确保更新了最后一个单元格。 Ctrl+ End将在保存之前进入错误的单元格(“直到保存工作表后,才会重置最后一个单元格”,来自 http://msdn.microsoft.com/zh-cn/library/aa139976%28v=office.10% 29.aspx,它是旧参考,但在这方面有效)。

对于条件3,我不知道任何内置方法。条件2不考虑条件格式。可能有一个基于公式的格式化的单元格,但未被UsedRangeCtrl+ 检测到End。在该图中,最后一个单元格是B3,因为格式化已明确应用于该单元格。单元格B6:D7的格式源自条件格式设置规则,即使也不检测到这种格式UsedRange。为此,需要进行一些VBA编程。

在此处输入图片说明


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

您的代码将E4:E48范围内的第一个单元格用作蹦床,与一起向下End(xlDown)

如果您的范围中除第一个单元格外没有其他非空白单元格,则将获得“错误”输出。然后,您将在黑暗跳来跳去,即跳下工作表(应注意空白字符串空字符串之间的区别!)。

注意:

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

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


3
我同意必须首先决定使用什么。我至少看到6个意思。单元格具有:1)数据,即公式,可能会导致空白值;2)一个值,即非空白公式或常数;3)格式化;4)条件格式;5)与单元格重叠的形状(包括注释);6)参与表(List Object)。您想测试哪种组合?有些(例如表格)可能更难测试,而有些则很少(例如数据范围之外的形状),但其他一些则可能根据情况而有所不同(例如,带有空白值的公式)。
GlennFromIowa

20

我创建了此一站式功能,用于确定最后一行,最后一列和单元格,无论是用于数据,已格式化(分组/注释/隐藏)的单元格还是条件格式

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

结果如下:
确定最后一个单元格

为了获得更详细的结果,代码中的某些行可以取消注释:
最后一列,行

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

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

2
@franklin-我刚注意到收件箱中有您的更正邮件,但被审核者拒绝。我纠正了这个错误。我已经在需要时使用了此功能一次,我将再次使用它,非常感谢,我的朋友!
ZygD

11

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

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

8

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

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


8

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

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

本质上,它返回选择Cell后通过Ctrl+ 得到的相同单元EndA1

请注意:Excel会跟踪工作表中曾经使用过的最右下角的单元格。因此,例如,如果您在B3中输入了某些内容,然后在H8中输入了其他内容,然后稍后删除H8的内容,则按Ctrl+ End仍会转到H8单元格。上面的功能将具有相同的行为。


2
Last Cell在Excel中,有时指的是(;)Used RangeLast Used Cell;)不同的空白单元格。
shA.t 2015年

1
OP只需要最后一行,但是您是对的,最后一个单元格应该为H5;但是您可以在删除A5中的值后测试功能。您将看到最后一个单元格是该空单元格,我认为您的代码需要进行一些编辑,因为那样可能Cells(1,1).Select()是无效的ActiveSheet.Cells(1,1).Select;同样在VBA中,不建议使用Select;)。
shA.t 2015年

5
这打破了Excel VBA的两个基本规则:不要使用Select!并且不要假设您想要的表是活动表。
Rachel Hettinger

1
这是一个旧答案,但缺少Set
BigBen

8

由于原来的问题是关于问题与找到的最后一个单元格,在这个答案我会列出你可以得到意想不到的效果的各种方法 ; 请参阅我的答案:“如何在包含宏的Excel工作表中找到包含数据的最后一行?” 我对解决这个问题的看法。

我会通过扩展开始由sancho.s答案,并通过GlennFromIowa注释,增加了更多的细节:

[...]首先必须决定使用什么。我至少看到6个意思。单元格具有:

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

您想测试哪种组合?有些(例如表格)可能更难测试,而有些则很少(例如数据范围之外的形状),但其他一些则可能根据情况而有所不同(例如,带有空白值的公式)。

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

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

考虑到这一点,让我们看看获取“最后一个单元格”的常见方法如何产生意外的结果:

  • 由于此处Siddharth Rout的答案(搜索“ xlDown同样不可靠”)中解释的原因.End(xlDown),该问题的代码最容易破坏(例如,使用单个非空单元格或之间有空白单元格
  • 任何基于Counting(CountACells*.Count)的解决方案,或者.CurrentRegion在存在空白单元格或行的情况下也会破裂👎
  • .End(xlUp)就像从CTRL + UP一样,从列尾开始向后搜索的解决方案将在可见行中查找数据(将产生空白值的公式视为“数据”)(因此在启用自动过滤器的情况下使用它可能会产生错误的结果⚠️ )。

    您必须注意避免出现标准陷阱(有关详细信息,我将再次参考Siddharth Rout的答案,在“查找列中的最后一行部分中进行查找),例如对最后一行(Range("A65536").End(xlUp))进行硬编码而不是依靠sht.Rows.Count

  • .SpecialCells(xlLastCell)等效于CTRL + END,返回“使用范围”的最底部和最右侧的单元格,因此所有依赖于“使用范围”的警告也适用于此方法。另外,“使用范围”仅在保存工作簿和访问时重置worksheet.UsedRange,因此xlLastCell可能会产生未保存的修改(例如,在删除某些行之后)陈旧的结果。通过dotNET查看附近的答案
  • sht.UsedRange(在sancho.s的答案中有详细说明),同时考虑了数据和格式设置(虽然不是条件格式),并重置了工作表的“使用范围”,这可能是您想要的,也可能不是。

    需要注意的是一个常见的错误️is到使用.UsedRange.Rows.Count⚠️,它返回的行数的使用范围,而不是最后的行号(他们会有所不同,如果前几行是空白),详见newguy的回答让我如何才能找到最后一行包含Excel工作表中带有宏的数据?

  • .Find允许您在任何列中查找包含任何数据(包括公式)或非空白值的最后一行。您可以选择对公式还是值感兴趣,但是要注意的是它会重置Excel的“查找”对话框中的默认值 ️️⚠️,这可能会使用户感到困惑。还需要谨慎使用它,请此处查看Siddharth Rout的答案“在工作表中查找最后一行”部分
  • Cells在循环中检查单个'的更明确的解决方案通常比重用Excel函数要慢(尽管仍然可以执行),但是让您精确地指定要查找的内容。请参阅我的基于UsedRange和VBA阵列的解决方案,以在给定列中查找包含数据的最后一个单元格-它处理隐藏的行,过滤器,空格,不修改“查找”默认值,并且性能很好。

无论选择哪种解决方案,请注意

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

4

但是这个问题试图使用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+ Enter使其成为数组公式。

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


1
我喜欢这个。我可能会稍作改动,以仅获取行号...'{= MATCH(INDEX(D:D,MAX(IF(D:D <>“”,ROW(D:D)-ROW(D1)+1)) ,1),D:D,0)}'
PGSystemTester

3
sub last_filled_cell()
msgbox range("A65536").end(xlup).row
end sub

A65536是A列中的最后一个单元格,此代码已在excel 2003上进行了测试。


您能解释一下您的代码如何回答这个老问题吗?
2015年

1
虽然此答案可能是正确且有用的,但如果您在其中包含一些解释以说明它如何帮助解决问题,则最好使用该解释。如果发生更改(可能不相关)导致它停止工作,并且用户需要了解它曾经如何工作,那么这在将来特别有用。
凯文·布朗

2

我一直在寻找一种模仿CTRL+ Shift+的方法End,因此dotNET解决方案很棒,除非set要避免出现错误,否则在Excel 2010中我需要添加一个:

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

1

这是我的两分钱。

恕我直言,隐藏行被排除数据的风险太大,以至于不能xlUp被认为是一站式答案。我同意这很简单,并且在大多数情况下都可以使用,但是它冒着低估最后一行的风险,而没有任何警告。这可能会产生CATASTROPHIC的一些poinit的人谁对堆栈过低跳下并期待“法子”捕捉到了这个值的结果。

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不会警告潜在的风险,他们可以继续为谁知道不知道自己的代码无法正常工作多久。


1

在过去的3年中,这些是我用来查找每个定义的列(对于行)和row(对于列)的最后一行和最后一列的函数:

最后一栏:

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,这是获取column中最后一行的方法E

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

最后一行,用数据计算空行:

在这里,我们可以使用众所周知的Excel公式,该公式为我们提供了Excel工作表的最后一行,而无需使用VBA-=IFERROR(LOOKUP(2,1/(NOT(ISBLANK(A:A))),ROW(A:A)),0)

为了将其放入VBA中而不使用Excel中的任何内容,请使用后一个函数的参数,应注意以下几点:

Public Function LastRowWithHidden(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

    Dim letters As String
    letters = ColLettersGenerator(columnToCheck)
    LastRowWithHidden = ws.Evaluate("=IFERROR(LOOKUP(2,1/(NOT(ISBLANK(" & letters & "))),ROW(" & letters & " )),0)")

End Function

Function ColLettersGenerator(col As Long) As String

    Dim result As Variant
    result = Split(Cells(1, col).Address(True, False), "$")
    ColLettersGenerator = result(0) & ":" & result(0)

End Function

如果最后一行/列被隐藏,这将返回错误的结果。
PGSystemTester

@PGSystemTester-是的,但是据我了解,当我对其编程时,如果它是隐藏的,则不需要最后一列/行。
Vityata

很高兴为您工作。我怀疑您的情况不是典型的用例。当我与需要最后一行的客户一起工作时,他们更频繁地搜索具有数据的最低单元,而不是具有数据的最低可见单元。无论如何...很高兴它起作用。👍–
PGSystemTester

@PGSystemTester-我明白你的意思,但是照顾好结构并且不允许看不见的单元看起来很吸引人。
Vityata '19

@PGSystemTester-是的,如果任务可能允许空行,我可能会使用EVAL()和著名的Excel公式。尽管人们可能认为这Eval()是邪恶的,但这是另一个
值得

0
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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.