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

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

Dim last_row As Integer Dim LastRow As Long LastRow = Range("E4:E48").End(xlDown).Row Debug.Print LastRow 

当我把一个元素放入一个单元格时,我得到了错误的输出。 但是,当我把多个值放入单元格时,输出是正确的。 这背后的原因是什么?

注意 :我打算使这是一个“一站式”职位,您可以使用Correct方式来查找最后一行。 这也将涵盖查找最后一行时要遵循的最佳实践。 因此,每当遇到新的场景/信息时,我都会继续更新它。


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

find最后一行非常不可靠的一些最常见的方法,因此不应该被使用。

  1. UsedRange
  2. xlDown
  3. COUNTA

UsedRange 永远不能用于find有数据的最后一个单元格。 这是非常不可靠的。 试试这个实验。

在单元格A5 。 现在当你用下面给出的任何方法计算最后一行时,它会给你5.现在给单元格A10红色。 如果你现在使用下面的代码,你仍然会得到5.如果你使用Usedrange.Rows.Count ,你会得到什么? 它不会是5。

这里是一个场景来显示UsedRange工作原理。

在这里输入图像描述

xlDown同样不可靠。

考虑这个代码

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

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

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

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


在列中查找最后一行

在Col E中find最后一行使用这个

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

如果你注意到我们有一个.Rows.Count之前。 我们经常select忽略这一点。 看到这个问题,你可能会得到的错误。 我总是build议使用.Columns.CountColumns.Count之前。 该问题是代码将失败的经典场景,因为Excel 2003和更早版本的Rows.Count返回65536 ,Excel 2007及更高版本返回1048576 。 同样, Columns.Count分别返回25616384

上述事实Excel 2007+有1048576行也强调了这样一个事实,我们应该总是声明variables,将行值保持为Long而不是Integer否则你会得到一个Overflow错误。


在工作表中查找最后一行

要查找工作表中的最后一行,请使用此。 注意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 

至于find最后一个使用的单元的正确方法,首先要决定什么是被使用的 ,然后select一个合适的方法 。 我至less有两个意思:

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

  2. 使用=“…在使用中,这意味着包含数据或格式的部分”。 这是Excel在保存时使用的标准 。 另见这个 。 这个标准通常会产生意想不到的结果,但是它也可能被有意地利用(不太经常,肯定),例如突出或打印可能最终没有数据的特定区域。 当然,在保存工作簿时,作为范围的标准是可取的。

如何find最后使用的细胞取决于想要什么(你的标准)

对于标准1,我build议阅读这个答案

对于标准2, UsedRange是最可靠的选项 。 它甚至不需要保存工作簿以确保最后一个单元格被更新。 在保存之前, Ctrl + End将转到错误的单元格(“保存工作表之前最后一个单元格没有被重置”, http://msdn.microsoft.com/en-us/library/aa139976%28v=office。; 10%29.aspx 。这是一个老的参考,但在这方面是有效的)。

还有另一个陷阱:
标准2不考虑条件格式。 根据公式,可能会有格式化的单元格,而这些公式不会被UsedRangeCtrl + End检测到。 在图中,最后一个单元格是B3,因为格式化是明确应用的。 单元格B6:D7具有从条件格式规则导出的格式,即使UsedRange也不会检测到。

在这里输入图像描述


至于你的具体问题这背后的原因是什么?

你的代码使用E4:E48中的第一个单元格作为蹦床,用End(xlDown) 跳下来。

如果您的范围内没有非空白单元格,可能会获得“错误”输出。 然后,你在黑暗跳跃 ,即在工作表中(你应该注意空白空string之间的区别!)。

注意:

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

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

我创build了这个一站式函数来确定最后一行,列和单元格,是数据,格式化(分组/注释/隐藏)单元格还是条件格式

 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 

我将添加到由Siddarth Rout给出的答案,说可以通过使Find返回Range对象而不是行号,然后testing返回的Range对象来查看它是否为Nothing(空白工作表),可以跳过CountA调用。 。

此外,我会有我的版本的任何LastRow过程返回一个空白工作表的零,然后我可以知道它是空白的。

使用该解决scheme时要牢记一个重要的注意事项…

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

…是为了确保您的LastRowvariables是Longtypes的:

 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 

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

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

这基本上是在select单元格A1后返回与Ctrl + End相同的单元格。

谨慎的一句话:Excel跟踪工作表中曾经使用过的最右下angular的单元格。 因此,例如,如果您在B3中input某些内容,然后在H8中input其他内容,然后再删除H8的内容,则按Ctrl + End仍然会将您带到H8单元格。 上面的函数将具有相同的行为。

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

“这里a65536是列中最后一个单元格,这个代码在excel sti72003”200上进行了testing

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

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

 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 

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

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

您需要input没有方括号的公式,然后点击Shitf + Ctrl + Enter使其成为一个数组公式。

这会给你最后使用的单元格的地址列D.

我正在寻找一种方法来模仿CTRL + SHIFT + END ,所以dotNET解决scheme是伟大的,除了我的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