Помилка пошуку останньої використаної комірки в Excel з VBA


179

Коли я хочу знайти останнє використане значення комірки, я використовую:

Dim LastRow As Long

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

Debug.Print LastRow

Я отримую неправильний вихід, коли вкладаю один елемент у комірку. Але коли я вкладаю більше, ніж одне значення в комірку, вихід правильний. У чому причина цього?


Відповіді:


309

ПРИМІТКА : Я маю намір перетворити це на "один стоп-пост", де ви можете використовувати Correctспосіб пошуку останнього ряду. Це також охопить найкращі практики, які слід дотримуватися при пошуку останнього ряду. А отже, я буду постійно оновлювати її, коли я натрапляю на новий сценарій / інформацію.


Ненадійні способи пошуку останнього ряду

Деякі з найпоширеніших способів пошуку останнього ряду, які є дуже ненадійними і тому ніколи не повинні використовуватися.

  1. Використовуваний
  2. xlDown
  3. CountA

UsedRangeповинні НІКОЛИ бути використані , щоб знайти останній осередок , яка має дані. Це дуже ненадійно. Спробуйте цей експеримент.

Наберіть щось у комірці A5. Тепер, коли ви обчислюєте останній рядок будь-яким із методів, наведених нижче, він дасть вам 5. Тепер пофарбуйте клітинку в A10червоний колір. Якщо ви зараз використовуєте будь-який із наведених нижче кодів, ви все одно отримаєте 5. Якщо ви користуєтесьUsedrange.Rows.Count те, що отримуєте? Це не буде 5.

Ось сценарій, який показує, як UsedRangeпрацює.

введіть тут опис зображення

xlDown однаково ненадійний.

Розглянемо цей код

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

Що буде, якби була лише одна клітинка ( A1), яка мала дані? Ви досягнете останнього ряду на робочому аркуші! Це як вибрати клітинку, A1а потім натиснути Endклавішу, а потім натиснути Down Arrowклавішу. Це також дасть недостовірні результати, якщо в діапазоні є порожні комірки.

CountA також ненадійний, оскільки це дасть неправильний результат, якщо між ними будуть порожні клітинки.

І , отже , слід уникати використання UsedRange, xlDownі CountAзнайти останню комірку.


Знайти останній рядок у стовпці

Щоб знайти останній рядок у Col E, скористайтеся цим

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

Якщо ви помітили, що у нас є .раніше Rows.Count. Ми часто вирішили це ігнорувати. Дивіться ЦЕ питання щодо можливої ​​помилки, яку ви можете отримати. Я завжди раджу використовувати .до Rows.Countі Columns.Count. Це питання є класичним сценарієм, коли код не вдасться, оскільки Rows.Countповернення 65536для Excel 2003 і раніше, а також 1048576для Excel 2007 і пізніших версій. Аналогічно Columns.Countповертається 256і 16384, відповідно.

Вищенаведений факт, що у Excel 2007+ має 1048576рядки, також наголошує на тому, що ми завжди повинні оголошувати змінну, яка буде містити значення рядка, оскільки Longзамість Integerіншого ви отримаєте Overflowпомилку.

Зауважте, що цей підхід буде пропускати будь-які приховані рядки. Озираючись на мій скріншот вище для стовпця А , якби рядок 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

6
Зверніть увагу, що .Find, на жаль, змінює налаштування користувача в діалоговому вікні Find - тобто у Excel є лише 1 набір налаштувань діалогового вікна, і ви, використовуючи .ind, замінює їх. Ще одна хитрість - все-таки використовувати UsedRange, але використовувати його як абсолютний (але ненадійний) максимум, з якого ви визначаєте правильний максимум.
Карл Колін

4
@CarlColijn: Я б не називав це безладом. :) Excel просто remembersостання настройка. Навіть коли ви робите це вручну Find, він пам’ятає останнє налаштування, яке насправді є користю, якщо хтось знає цей «факт»
Siddharth Rout

3
@KeithPark: Будь ласка, продовжуйте :) Знання має сенс лише в тому випадку, якщо воно поширюється :)
Siddharth Rout

9
Я думаю , що ваше опис UsedRange( вельми ненадійні , щоб знайти останній осередок , яка має дані ) вводить в оману. UsedRangeпросто не призначений для цієї мети, хоча в деяких випадках це може дати правильний результат. Я думаю, що запропонований експеримент додає плутанини. Результат, отриманий за допомогою UsedRange($ A $ 1: $ A $ 8), не залежить від першого введення даних про їх видалення. Цифра праворуч буде залишатися такою самою, навіть не вводивши її видалені дані. Будь ласка, дивіться мою відповідь.
sancho.s ReinstateMonicaCellio

34

Примітка: цю відповідь мотивував цим коментарем . Мета UsedRangeвідрізняється від зазначеної у відповіді вище.

Що стосується правильного способу пошуку останньої використовуваної комірки, спочатку слід вирішити, що вважається використаним , а потім вибрати відповідний метод . Я уявляю щонайменше три значення:

  1. Використовується = непорожня, тобто має дані .

  2. Використовується = "... використовується, тобто розділ, що містить дані або форматування ." Відповідно до офіційної документації , це критерій, який використовував Excel під час збереження. Дивіться також цю офіційну документацію . Якщо хтось цього не знає, критерій може призвести до несподіваних результатів, але він також може бути навмисно використаний (рідше, напевно), наприклад, для виділення або друку конкретних регіонів, які, зрештою, не мають даних. І, звичайно, бажано в якості критерію діапазону використовувати під час збереження робочої книги, щоб не втратити частину своєї роботи.

  3. Використовується = "... використовується, означає розділ, що містить дані або форматування " або умовне форматування. Те саме, що є 2., але також включає комірки, які є ціллю для будь-якого правила умовного форматування.

Як знайти останню використану клітинку, залежить від того, що ви хочете (від вашого критерію) .

Для критерію 1 пропоную прочитати цю відповідь . Зауважте, що UsedRangeце цитується як ненадійний. Я думаю, що це вводить в оману (тобто "несправедливо" щодо UsedRange), оскільки UsedRangeпросто не мається на увазі повідомляти про останню клітинку, що містить дані. Тому його не слід використовувати в цьому випадку, як зазначено у цій відповіді. Дивіться також цей коментар .

Критерій 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.

введіть тут опис зображення


Що стосується вашого конкретного питання : у чому причина цього?

Ваш код використовує першу клітинку у вашому діапазоні E4: E48 як батут, щоб стрибати вниз End(xlDown).

"Помилковий" результат вийде, якщо у вашому діапазоні немає інших порожніх комірок, крім, можливо, перших. Потім ви стрибаєте в темряві , тобто вниз по робочому аркушу (ви повинні відзначити різницю між порожнім і порожнім рядком !).

Зауважте, що:

  1. Якщо ваш діапазон містить непомітні непорожні клітини, то це також дасть неправильний результат.

  2. Якщо є лише одна порожня клітинка, але вона не перша, ваш код все одно дасть правильний результат.


3
Я погоджуюся, що спочатку потрібно вирішити, що вважати вживаним . Я бачу щонайменше 6 значень. В комірці є: 1) дані, тобто формула, можливо, в результаті виходить порожнє значення; 2) значення, тобто порожня формула або константа; 3) форматування; 4) умовне форматування; 5) форма (включаючи коментар), що перекриває клітинку; 6) залучення до таблиці (об’єкт списку). На яку комбінацію ви хочете протестувати? Деякі (наприклад, таблиці) можуть бути складнішими для перевірки, а деякі можуть бути рідкісними (наприклад, форма поза межами діапазону даних), але інші можуть змінюватися залежно від ситуації (наприклад, формули з порожніми значеннями).
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

Це моя інкапсульована функція, яку я впадаю в різні використання коду.

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, щоб сказати, що виклик CountA можна пропустити, якщо знайти повернути об'єкт Range, а не номер рядка, а потім протестувати повернутий об'єкт Range, щоб побачити, чи немає нічого (порожній робочий аркуш) .

Крім того, я мав би свою версію будь-якої процедури LastRow повернути нуль для порожнього робочого аркуша, тоді я можу знати, що він порожній.


8

Цікаво, що ніхто про це не згадував, але найпростіший спосіб отримати останню використану клітинку:

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

Це по суті повертає ту саму клітинку, яку ви отримуєте за Ctrl+ Endпісля вибору Cell A1.

Слово застереження: Excel відслідковує саму нижню праву клітинку, яка коли-небудь використовувалася на робочому аркуші. Отже, якщо, наприклад, ви введете щось у B3 і щось інше в H8, а потім видалите вміст H8 , натискання Ctrl+ Endвсе одно переведе вас до комірки H8 . Вищевказана функція матиме таку саму поведінку.


2
Last Cellв Excel іноді посилається на порожню клітинку (від Used Range), яка відрізняється від Last Used Cell;).
shA.t

1
ОП потребував лише останній ряд, але ви маєте рацію, остання комірка повинна бути H5 ; Але ви можете перевірити свою функцію після видалення значення в A5. Ви побачите, що остання комірка - це порожня комірка, і я думаю, що ваш код потребує деяких редагувань, таких Cells(1,1).Select()як недійсних, можливо, це є ActiveSheet.Cells(1,1).Select; Також у VBA не рекомендується використовувати Select;).
shA.t

5
Це порушує два основні правила для Excel VBA: Не використовуйте Select! І не вважайте, що аркуш, який ви хочете, є активним.
Рейчел Хеттінгер

1
Це стара відповідь, але її немає Set.
BigBen

8

Оскільки оригінальне запитання стосується проблем із знаходженням останньої комірки, у цій відповіді я перерахую різні способи отримання несподіваних результатів ; дивіться мою відповідь на "Як я можу знайти останній рядок, який містить дані на листі Excel з макросом?" на мій погляд на вирішення цього питання.

Почну з розширення відповіді від sancho.s та коментаря GlennFromIowa , додавши ще більше деталей:

[...] спершу слід вирішити, що вважати вживаним. Я бачу щонайменше 6 значень. У комірці є:

  • 1) дані, тобто формула, яка, можливо, призводить до порожнього значення;
  • 2) значення, тобто порожня формула або константа;
  • 3) форматування;
  • 4) умовне форматування;
  • 5) форма (включаючи коментар), що перекриває клітинку;
  • 6) залучення до таблиці (об’єкт списку).

На яку комбінацію ви хочете протестувати? Деякі (наприклад, таблиці) можуть бути складнішими для перевірки, а деякі можуть бути рідкісними (наприклад, форма поза межами діапазону даних), але інші можуть змінюватися залежно від ситуації (наприклад, формули з порожніми значеннями).

Інші речі, які ви можете розглянути:

  • A) Чи можуть бути приховані рядки (наприклад, автофільтр), порожні комірки або порожні рядки?
  • Б) Який вигляд є прийнятним?
  • C) Чи може макрос VBA впливати будь-яким чином на робочу книгу або на налаштування програми?

Зважаючи на це, давайте подивимося, як загальні способи отримання "останньої комірки" можуть дати несподівані результати:

  • .End(xlDown)Код від питання зламається найбільш легко (наприклад , з однієї непорожній осередку або коли є порожні клітинки між ) з причин , викладених у відповіді за Сиддхарт Втеча тут (пошук по «xlDown однаково ненадійні.» ) 👎
  • Будь-яке рішення, засноване на Counting ( CountAабо Cells*.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 тут) враховує як дані, так і форматування (хоча не умовне форматування) та скидає "використаний діапазон" робочого аркуша , який може бути, а може і не бути тим, що ви хочете.

    Зауважте, що поширеною помилкою є використання .UsedRange.Rows.Count⚠️, яка повертає кількість рядків у використаному діапазоні, а не номер останнього рядка (вони будуть різними, якщо перші кілька рядків порожні), для детальної інформації див відповідь newguy на те, як я можу знайти останній рядок, який містить дані на аркуші Excel з макросом?

  • .Findдозволяє знайти останній рядок з будь-якими даними (включаючи формули) або непорожнім значенням у будь-якому стовпці . Ви можете вибрати, чи цікавлять ви формули чи значення, але суть полягає в тому, що він скидає параметри за замовчуванням у діалоговому вікні пошуку Excel ️️⚠️, що може дуже заплутати користувачів. Це також потрібно обережно використовувати, дивіться відповідь Сіддхарта Рута тут (розділ "Знайти останній рядок у аркуші" )
  • Більш чіткі рішення, які перевіряють особу Cells"в циклі, як правило, повільніше, ніж повторне використання функції Excel (хоча вона все ще може бути виконаною), але дозволяють точно вказати, що ви хочете знайти. Дивіться моє рішення на основі UsedRangeі масивів VBA, щоб знайти останню комірку з даними в даному стовпці - вона обробляє приховані рядки, фільтри, пробіли, не змінює параметри Find за замовчуванням і є досить ефективною.

Яке б рішення ви не вибрали, будьте обережні

  • використовувати Longзамість того, Integerщоб зберігати номери рядків (щоб уникнути отриманняOverflow більше 65 крок) та
  • завжди вказувати робочий аркуш, з яким ви працюєте (тобто 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остання клітинка у колонці А. Цей код був перевірений у програмі excel 2003.


Чи можете ви пояснити, як ваш код відповідає на це старе запитання?
шовер

1
Хоча ця відповідь, ймовірно, правильна і корисна, бажано, якщо ви додасте разом із нею якесь пояснення, щоб пояснити, як це допомагає вирішити проблему. Це стане особливо корисним у майбутньому, якщо відбудеться якась зміна (можливо непов'язана), яка змусить її перестати працювати, і користувачі повинні зрозуміти, як це колись працювало.
Кевін Браун

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

1

Ось два мої центи.

IMHO ризик прихованого рядка із виключенням даних є надто значним, щоб його xlUpможна було вважати однозначною відповіддю . Я погоджуюся, що це просто і працюватиме НАЙБІЛЬШ час, але це створює ризик заниження останнього ряду, без будь-якого попередження. Це може призвести до КАТАСТРОФІЧНОГО результатів РЕЗУЛЬТАТУ для когось, хто стрибнув на стек Overlow і шукав "вірний шлях" захопити це значення.

FindМетод є бездоганним , і я схвалюю його як One Stop Відповідь . Однак недолік зміни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+ років це функції, які я використовую для пошуку останнього рядка та останнього стовпця для визначеного стовпця (для рядка) та рядка (для стовпця):

Останній стовпець:

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

У випадку з ОП це спосіб отримати останній рядок у стовпці 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 - так, але, на моє розуміння, коли я його програмую, якщо він прихований, це не останній стовпець / рядок, який потрібен.
Вітята

Радий, що працює на тебе. Я підозрюю, що ваша ситуація не є типовим випадком використання. Частіше, коли я працюю з клієнтами, яким потрібен останній рядок, вони шукають найнижчу клітинку з даними, а не найнижчу видиму клітинку з даними. У всякому разі ... рада, що це працює. 👍
PGSystemTester

@PGSystemTester - я зрозумів, але турбота про структуру та не дозволяючи невидимим клітинкам працює як принадність.
Вітята

@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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.