【问题标题】:How can I find last row that contains data in a specific column?如何找到包含特定列中数据的最后一行?
【发布时间】:2010-09-09 10:17:06
【问题描述】:

如何找到包含特定列和特定工作表中数据的最后一行?

【问题讨论】:

标签: excel vba


【解决方案1】:

怎么样:

Function GetLastRow(strSheet, strColumn) As Long
    Dim MyRange As Range

    Set MyRange = Worksheets(strSheet).Range(strColumn & "1")
    GetLastRow = Cells(Rows.Count, MyRange.Column).End(xlUp).Row
End Function

关于注释,即使最后一行中只有一个单元格有数据,这也会返回最后一个单元格的行号:

Cells.Find("*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row

【讨论】:

  • 此函数将在以下情况下返回错误结果: - strColumn 是一个数字 - 第 65536 行中有一些数据 - 您正在使用超过 65536 行的 Excel 2007
  • @GSerg 你可以更正代码,然后如果你喜欢删除评论(因为它不再适用
  • 如果单元格有数据然后删除了数据,我相信这会错误地报告哪个单元格是最后一个单元格。
  • Siddharth Rout's answer to "Error in finding last used cell in VBA" 详细阐述了这两种方法,并指出了一些问题,例如使用Rows.Count(请参阅“查找列中的最后一行”和“查找工作表中的最后一行”); my answer to that question 列出了使用这些的一些可能的意外后果(即,不能使用自动过滤器和.Find 与 Excel 的“查找”对话框中的默认值混淆)。
  • Cells.Find 函数是我一直需要的!厌倦了确定列,我想要一个带有左上/右上和底部使用的行的范围!
【解决方案2】:

您应该使用.End(xlup),但您可能想要使用而不是使用 65536:

sheetvar.Rows.Count

这样它适用于 Excel 2007,我相信它有超过 65536 行

【讨论】:

  • 这似乎是对@Fionnuala 答案的评论。另请注意,xlUp 从一列的末尾向后搜索,就好像您按下了 CTRL+UP,因此它可能会在存在隐藏行的情况下产生不正确的结果(例如,启用自动过滤器)。
【解决方案3】:

简单快捷:

Dim lastRow as long
Range("A1").select
lastRow = Cells.Find("*",SearchOrder:=xlByRows,SearchDirection:=xlPrevious).Row

使用示例:

cells(lastRow,1)="Ultima Linha, Last Row. Youpi!!!!"

'or 

Range("A" & lastRow).Value = "FIM, THE END"

【讨论】:

  • 或者像这样'function getSheetLastRow(sheet2Check as worksheet) lastRow = sheet2Check .Cells.Find("*",SearchOrder:=xlByRows,SearchDirection:=xlPrevious).Row getSheetLastRow=lastRow end function'
  • 这不限于所要求的问题的特定列;它还使用Select 并访问Range/Cells 而不指定工作表对象,这被认为是不好的样式。 Siddharth Rout's answer to "Error in finding last used cell in VBA" 的“查找工作表中的最后一行”部分有更好的解决方案,如果您觉得必须使用 .Find...
【解决方案4】:
function LastRowIndex(byval w as worksheet, byval col as variant) as long
  dim r as range

  set r = application.intersect(w.usedrange, w.columns(col))
  if not r is nothing then
    set r = r.cells(r.cells.count)

    if isempty(r.value) then
      LastRowIndex = r.end(xlup).row
    else
      LastRowIndex = r.row
    end if
  end if
end function

用法:

? LastRowIndex(ActiveSheet, 5)
? LastRowIndex(ActiveSheet, "AI")

【讨论】:

  • 真的需要检查 isempty(r.value) 吗?它不应该总是有价值吗?
  • @gdelfino 是的。例如。 A 列的值在第 1-10 行,B 列的值在第 1-8 行。 UsedRangeA1:B10,与B:B 的交集为B1:B10,最后一个单元格为B10,为空。
  • 如果您仍然使用.end(xlup),为什么要获取UsedRange 的最后一行而不是从工作表的最后一行(sht.Rows.Count) 开始会更加复杂?我相信如果 UsedRange 不是从第一行开始(即第一行为空),那么获取最后一行的方式将失败。
  • @Nickolay 因为从工作表的最后一行开始是基于工作表最后一行为空的错误假设。它经常是,但并非总是如此。我对这样的错误假设不满意。只有当该列完全为空时,它才会跳过原始的UsedRange,这是正确的行为,因为此处定义列表的仅有两个变量是工作表和列号,即exactly how the OP worded the problem
  • 不,这是支持if isempty(r.value) 检查的论点——我对此表示赞赏。您可以从最后一行开始,仍然进行检查,但在此之前保存 5 行(同时删除错误的计数逻辑)——最终得到更强大的 @ 的“在列中查找最后一行”部分的版本987654322@(仍然不处理自动过滤器,但对于某些用例来说还可以)。除非这是一种优化,我认为没有必要,但我想我会先问。
【解决方案5】:
Public Function LastData(rCol As Range) As Range    
    Set LastData = rCol.Find("*", rCol.Cells(1), , , , xlPrevious)    
End Function

用法:?lastdata(activecell.EntireColumn).Address

【讨论】:

    【解决方案6】:

    所有依赖于内置行为的解决方案(如.Find.End)都存在一些没有详细记录的限制(有关详细信息,请参阅my other answer)。

    我需要一些东西:

    • 特定列中查找最后一个非空单元格(即具有任何公式或值,即使它是空字符串) >
    • 依赖于具有明确行为的原语
    • 可靠地使用自动过滤器和用户修改
    • 在 10,000 行上尽可能快地运行(在 Worksheet_Change 处理程序中运行而不会感到迟钝)
    • ...性能不会因为意外数据或格式放在工作表的最后(约 1M 行)而跌落悬崖

    解决方法如下:

    • 使用UsedRange 查找行号的上限(以便在接近使用范围末尾的常见情况下快速搜索真正的“最后一行”);
    • 向后查找给定列中包含数据的行;
    • ...使用 VBA 数组来避免单独访问每一行(如果UsedRange 中有很多行,我们需要跳过)

    (没有测试,抱歉)

    ' Returns the 1-based row number of the last row having a non-empty value in the given column (0 if the whole column is empty)
    Private Function getLastNonblankRowInColumn(ws As Worksheet, colNo As Integer) As Long
        ' Force Excel to recalculate the "last cell" (the one you land on after CTRL+END) / "used range"
        ' and get the index of the row containing the "last cell". This is reasonably fast (~1 ms/10000 rows of a used range)
        Dim lastRow As Long: lastRow = ws.UsedRange.Rows(ws.UsedRange.Rows.Count).Row - 1 ' 0-based
    
        ' Since the "last cell" is not necessarily the one we're looking for (it may be in a different column, have some
        ' formatting applied but no value, etc), we loop backward from the last row towards the top of the sheet).
        Dim wholeRng As Range: Set wholeRng = ws.Columns(colNo)
    
        ' Since accessing cells one by one is slower than reading a block of cells into a VBA array and looping through the array,
        ' we process in chunks of increasing size, starting with 1 cell and doubling the size on each iteration, until MAX_CHUNK_SIZE is reached.
        ' In pathological cases where Excel thinks all the ~1M rows are in the used range, this will take around 100ms.
        ' Yet in a normal case where one of the few last rows contains the cell we're looking for, we don't read too many cells.
        Const MAX_CHUNK_SIZE = 2 ^ 10 ' (using large chunks gives no performance advantage, but uses more memory)
        Dim chunkSize As Long: chunkSize = 1
        Dim startOffset As Long: startOffset = lastRow + 1 ' 0-based
        Do ' Loop invariant: startOffset>=0 and all rows after startOffset are blank (i.e. wholeRng.Rows(i+1) for i>=startOffset)
            startOffset = IIf(startOffset - chunkSize >= 0, startOffset - chunkSize, 0)
            ' Fill `vals(1 To chunkSize, 1 To 1)` with column's rows indexed `[startOffset+1 .. startOffset+chunkSize]` (1-based, inclusive)
            Dim chunkRng As Range: Set chunkRng = wholeRng.Resize(chunkSize).Offset(startOffset)
            Dim vals() As Variant
            If chunkSize > 1 Then
                vals = chunkRng.Value2
            Else ' reading a 1-cell range requires special handling <http://www.cpearson.com/excel/ArraysAndRanges.aspx>
                ReDim vals(1 To 1, 1 To 1)
                vals(1, 1) = chunkRng.Value2
            End If
    
            Dim i As Long
            For i = UBound(vals, 1) To LBound(vals, 1) Step -1
                If Not IsEmpty(vals(i, 1)) Then
                    getLastNonblankRowInColumn = startOffset + i
                    Exit Function
                End If
            Next i
    
            If chunkSize < MAX_CHUNK_SIZE Then chunkSize = chunkSize * 2
        Loop While startOffset > 0
    
        getLastNonblankRowInColumn = 0
    End Function
    

    【讨论】:

    • 这种技术永远不会给出错误的答案,而且是最好的!我自己更喜欢 XML 方式
    【解决方案7】:

    这是查找最后一行、最后一列或最后一个单元格的解决方案。它解决了它找到的列的 A1 R1C1 参考样式困境。希望我能给予信任,但找不到/记得我从哪里得到它,所以“谢谢!”发给在某处发布原始代码的人。

    Sub Macro1
        Sheets("Sheet1").Select
        MsgBox "The last row found is: " & Last(1, ActiveSheet.Cells)
        MsgBox "The last column (R1C1) found is: " & Last(2, ActiveSheet.Cells)
        MsgBox "The last cell found is: " & Last(3, ActiveSheet.Cells)
        MsgBox "The last column (A1) found is: " & Last(4, ActiveSheet.Cells)
    End Sub
    
    Function Last(choice As Integer, rng As Range)
    ' 1 = last row
    ' 2 = last column (R1C1)
    ' 3 = last cell
    ' 4 = last column (A1)
        Dim lrw As Long
        Dim lcol As Integer
    
        Select Case choice
        Case 1:
            On Error Resume Next
            Last = rng.Find(What:="*", _
                            After:=rng.Cells(1), _
                            LookAt:=xlPart, _
                            LookIn:=xlFormulas, _
                            SearchOrder:=xlByRows, _
                            SearchDirection:=xlPrevious, _
                            MatchCase:=False).Row
            On Error GoTo 0
    
        Case 2:
            On Error Resume Next
            Last = rng.Find(What:="*", _
                            After:=rng.Cells(1), _
                            LookAt:=xlPart, _
                            LookIn:=xlFormulas, _
                            SearchOrder:=xlByColumns, _
                            SearchDirection:=xlPrevious, _
                            MatchCase:=False).Column
            On Error GoTo 0
    
        Case 3:
            On Error Resume Next
            lrw = rng.Find(What:="*", _
                           After:=rng.Cells(1), _
                           LookAt:=xlPart, _
                           LookIn:=xlFormulas, _
                           SearchOrder:=xlByRows, _
                           SearchDirection:=xlPrevious, _
                           MatchCase:=False).Row
            lcol = rng.Find(What:="*", _
                            After:=rng.Cells(1), _
                            LookAt:=xlPart, _
                            LookIn:=xlFormulas, _
                            SearchOrder:=xlByColumns, _
                            SearchDirection:=xlPrevious, _
                            MatchCase:=False).Column
            Last = Cells(lrw, lcol).Address(False, False)
            If Err.Number > 0 Then
                Last = rng.Cells(1).Address(False, False)
                Err.Clear
            End If
            On Error GoTo 0
        Case 4:
            On Error Resume Next
            Last = rng.Find(What:="*", _
                            After:=rng.Cells(1), _
                            LookAt:=xlPart, _
                            LookIn:=xlFormulas, _
                            SearchOrder:=xlByColumns, _
                            SearchDirection:=xlPrevious, _
                            MatchCase:=False).Column
            On Error GoTo 0
            Last = R1C1converter("R1C" & Last, 1)
            For i = 1 To Len(Last)
                s = Mid(Last, i, 1)
                If Not s Like "#" Then s1 = s1 & s
            Next i
            Last = s1
    
        End Select
    
    End Function
    
    Function R1C1converter(Address As String, Optional R1C1_output As Integer, Optional RefCell As Range) As String
        'Converts input address to either A1 or R1C1 style reference relative to RefCell
        'If R1C1_output is xlR1C1, then result is R1C1 style reference.
        'If R1C1_output is xlA1 (or missing), then return A1 style reference.
        'If RefCell is missing, then the address is relative to the active cell
        'If there is an error in conversion, the function returns the input Address string
        Dim x As Variant
        If RefCell Is Nothing Then Set RefCell = ActiveCell
        If R1C1_output = xlR1C1 Then
            x = Application.ConvertFormula(Address, xlA1, xlR1C1, , RefCell) 'Convert A1 to R1C1
        Else
            x = Application.ConvertFormula(Address, xlR1C1, xlA1, , RefCell) 'Convert R1C1 to A1
        End If
        If IsError(x) Then
            R1C1converter = Address
        Else
            'If input address is A1 reference and A1 is requested output, then Application.ConvertFormula
            'surrounds the address in single quotes.
            If Right(x, 1) = "'" Then
                R1C1converter = Mid(x, 2, Len(x) - 2)
            Else
                x = Application.Substitute(x, "$", "")
                R1C1converter = x
            End If
        End If
    End Function
    

    【讨论】:

      【解决方案8】:

      我想添加一种更可靠的方法,使用UsedRange 来查找最后使用的行:

      lastRow = Sheet1.UsedRange.Row + Sheet1.UsedRange.Rows.Count - 1
      

      同样可以找到上次使用的列see this

      立即窗口中的结果:

      ?Sheet1.UsedRange.Row+Sheet1.UsedRange.Rows.Count-1
       21 
      

      【讨论】:

      • 注意UsedRange 也会拾取公式,如果您将公式拖到可见数据(或even formatting)下方可能会出现问题。
      • @micstr 是的,但其他所有方法都会这样做。
      【解决方案9】:
      Public Function GetLastRow(ByVal SheetName As String) As Integer
          Dim sht As Worksheet
          Dim FirstUsedRow As Integer     'the first row of UsedRange
          Dim UsedRows As Integer         ' number of rows used
      
          Set sht = Sheets(SheetName)
          ''UsedRange.Rows.Count for the empty sheet is 1
          UsedRows = sht.UsedRange.Rows.Count
          FirstUsedRow = sht.UsedRange.Row
          GetLastRow = FirstUsedRow + UsedRows - 1
      
          Set sht = Nothing
      End Function
      

      sheet.UsedRange.Rows.Count:返回使用的行数,不包括使用的第一行上方的空行

      如果第 1 行为空,并且最后使用的行是 10,则 UsedRange.Rows.Count 将返回 9,而不是 10。

      此函数计算 UsedRange 的第一行数加上 UsedRange 行数。

      【讨论】:

      • 我觉得这在很大程度上重复了other answer by newguy。另请注意,使用Integer 而不是Long 作为行号可能会在工作表大于65k 行时遇到Overflow 错误。
      【解决方案10】:
      Last_Row = Range("A1").End(xlDown).Row
      

      只是为了验证,假设您想用单元格 C1 中的数据打印最后一行的行号。

      Range("C1").Select
      Last_Row = Range("A1").End(xlDown).Row
      ActiveCell.FormulaR1C1 = Last_Row
      

      【讨论】:

        【解决方案11】:

        使用二分查找

        获取最后一个非空行
        • 虽然有隐藏值,但返回正确的值事件
        • 如果在最后一个非空单元格之前有空单元格(例如第 5 行是空的,但第 10 行是最后一个非空行),则可能返回不正确的值
        Function getLastRow(col As String, ws As Worksheet) As Long
            Dim lastNonEmptyRow As Long
            lastNonEmptyRow = 1
            Dim lastEmptyRow As Long
        
            lastEmptyRow = ws.Rows.Count + 1
            Dim nextTestedRow As Long
            
            Do While (lastEmptyRow - lastNonEmptyRow > 1)
                nextTestedRow = Application.WorksheetFunction.Ceiling _
                    (lastNonEmptyRow + (lastEmptyRow - lastNonEmptyRow) / 2, 1)
                If (IsEmpty(ws.Range(col & nextTestedRow))) Then
                    lastEmptyRow = nextTestedRow
                Else
                    lastNonEmptyRow = nextTestedRow
                End If
            Loop
            
            getLastRow = lastNonEmptyRow
            
        
        End Function
        

        【讨论】:

          【解决方案12】:
          Function LastRow(rng As Range) As Long
              Dim iRowN As Long
              Dim iRowI As Long
              Dim iColN As Integer
              Dim iColI As Integer
              iRowN = 0
              iColN = rng.Columns.count
              For iColI = 1 To iColN
                  iRowI = rng.Columns(iColI).Offset(65536 - rng.Row, 0).End(xlUp).Row
                  If iRowI > iRowN Then iRowN = iRowI
              Next
              LastRow = iRowN
          End Function 
          

          【讨论】:

            【解决方案13】:
            Sub test()
                MsgBox Worksheets("sheet_name").Range("A65536").End(xlUp).Row
            End Sub
            

            由于"A65536",这是在A 列中查找值。

            【讨论】:

              【解决方案14】:

              第一行将光标移动到列中的最后一个非空行。第二行打印该列的行。

              Selection.End(xlDown).Select
              MsgBox(ActiveCell.Row)
              

              【讨论】:

              • 如果列中有空白单元格,这不起作用。
              猜你喜欢
              • 2023-01-31
              • 2021-01-10
              • 2022-12-29
              • 1970-01-01
              • 2023-02-23
              • 1970-01-01
              • 2016-05-30
              • 1970-01-01
              相关资源
              最近更新 更多