Как найти последнюю строку, содержащую данные в листе Excel с макросом?

56

Как я могу найти последнюю строку, содержащую данные в определенном столбце и на определенном листе?

Показать ещё 2 комментария
Теги:
excel-vba
excel

13 ответов

44
Лучший ответ

Как насчет:

 Sub GetLastRow(strSheet, strColumn)
 Dim MyRange As Range
 Dim lngLastRow As Long

    Set MyRange = Worksheets(strSheet).Range(strColumn & "1")

    lngLastRow = Cells(Rows.Count, MyRange.Column).End(xlUp).Row
 End Sub

Re Comment

это

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

Будет возвращать номер строки последней ячейки, даже если только одна ячейка в последней строке имеет данные.

  • 9
    Эта функция будет возвращать неверные результаты, когда: - strColumn - число - в строке 65536 есть данные - вы используете Excel 2007 с более чем 65536 строками
  • 1
    @GSerg Вы можете исправить код, а затем удалить комментарий, если хотите (так как он больше не будет применяться <g>)
Показать ещё 2 комментария
21

Вы должны использовать .End(xlup), но вместо использования 65536 вы можете использовать:

sheetvar.Rows.Count

Таким образом, он работает в Excel 2007, который, как мне кажется, содержит более 65536 строк

7

Простой и быстрый:

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"
  • 0
    Или вот так: функция getSheetLastRow (sheet2Check as worksheet) lastRow = sheet2Check .Cells.Find ("*", SearchOrder: = xlByRows, SearchDirection: = xlPrevious) .Row getSheetLastRow = lastRow end function '
  • 0
    Это не ограничивается конкретным столбцом в качестве запрошенного вопроса; он также использует Select и обращается к Range / Cells без указания объекта листа, что считается плохим стилем. Раздел «Найти последнюю строку в листе» ответа Сиддхарта Раута на «Ошибка при поиске последней использованной ячейки в VBA» имеет лучшее решение, если вы считаете, что должны использовать .Find ...
7
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")
  • 0
    Действительно ли нужна проверка на isempty (r.value)? Разве это не должно всегда иметь значение?
  • 0
    @gdelfino Да, это так. Например, столбец A имеет значения в строках 1-10, столбец B имеет значения в строках 1-8. UsedRange будет A1:B10 , пересечение с B:B будет B1:B10 , последняя ячейка - B10 и она пуста.
Показать ещё 5 комментариев
3

Здесь найдено решение для поиска последней строки, последнего столбца или последней ячейки. Он обращается к дилемме ссылочного стиля 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
3
Public Function LastData(rCol As Range) As Range    
    Set LastData = rCol.Find("*", rCol.Cells(1), , , , xlPrevious)    
End Function

Использование: ?lastdata(activecell.EntireColumn).Address

2

Все решения, основанные на встроенных поведении (например .Find и .End), имеют ограничения, которые недостаточно хорошо документированы (подробности см. В моем другом ответе).

Мне нужно было что-то такое:

  • Находит последнюю непустую ячейку (т.е. Имеет любую формулу или значение, даже если это пустая строка) в определенном столбце
  • Опирается на примитивы с четко определенным поведением
  • Хорошо работает с автофильтрами и модификациями пользователей
  • Выполняется как можно быстрее на 10 000 строк (для запуска в обработчике Worksheet_Change без ощущения вялости)
  • ... с производительностью, не падающей с утеса с случайными данными или форматированием, помещенными в самый конец листа (на ~ 1М строк)

Решение ниже:

  • Использует UsedRange чтобы найти верхнюю границу для номера строки (чтобы 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 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
1

Я хотел бы добавить еще один надежный способ, используя UsedRange, чтобы найти последнюю использованную строку:

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

Аналогично, чтобы найти последний использованный столбец, вы можете видеть это

Изображение 3744

Результат в непосредственном окне:

?Sheet1.UsedRange.Row+Sheet1.UsedRange.Rows.Count-1
 21 
  • 0
    Примечание. UsedRange также выбирает формулы, которые могут быть проблемой, если формулы перетаскиваются ниже видимых данных (или даже форматируются ).
  • 0
    @micstr Правда, но любой другой метод сделает это.
0
  Last_Row = Range("A1").End(xlDown).Row

Просто чтобы проверить, допустим, вы хотите напечатать номер строки последней строки с данными в ячейке C1.

Range("C1").Select
Last_Row = Range("A1").End(xlDown).Row
ActiveCell.FormulaR1C1 = Last_Row
0
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.

  • 0
    Я чувствую, что это в значительной степени дублирует другой ответ newguy . Также обратите внимание, что использование Integer вместо Long для номеров строк может Overflow ошибке Overflow листов размером более 65 000 строк.
0
sub test()
msgbox Worksheets("sheet_name").Range("A65536").End(xlUp).Row
end sub

Это поиск значения в столбце A из-за "A65536"

-1
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 
-2

Первая функция перемещает курсор в последнюю непустую строку в столбце. Вторая функция печатает эту строку столбцов.

Selection.End(xlDown).Select
MsgBox (ActiveCell.Row)
  • 6
    Это не работает, если в столбце есть пустые ячейки.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню