Fehler beim Auffinden der zuletzt verwendeten Zelle in Excel mit VBA

179

Wenn ich den zuletzt verwendeten Zellenwert finden möchte, verwende ich:

Dim LastRow As Long

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

Debug.Print LastRow

Ich erhalte die falsche Ausgabe, wenn ich ein einzelnes Element in eine Zelle einfüge. Wenn ich jedoch mehr als einen Wert in die Zelle eingebe, ist die Ausgabe korrekt. Was ist der Grund dafür?

James
quelle

Antworten:

309

HINWEIS : Ich beabsichtige, dies zu einem "One-Stop-Post" zu machen Correct, auf dem Sie die letzte Zeile finden können. Dies umfasst auch die Best Practices, die beim Auffinden der letzten Zeile zu befolgen sind. Und daher werde ich es weiter aktualisieren, wenn ich auf ein neues Szenario / eine neue Information stoße.


Unzuverlässige Wege, um die letzte Zeile zu finden

Einige der gebräuchlichsten Methoden zum Auffinden der letzten Zeile, die sehr unzuverlässig sind und daher niemals verwendet werden sollten.

  1. UsedRange
  2. xlDown
  3. CountA

UsedRangesollte NIEMALS verwendet werden, um die letzte Zelle mit Daten zu finden. Es ist sehr unzuverlässig. Versuchen Sie dieses Experiment.

Geben Sie etwas in die Zelle ein A5. Wenn Sie nun die letzte Zeile mit einer der unten angegebenen Methoden berechnen, erhalten Sie 5. Färben Sie nun die Zelle A10rot. Wenn Sie jetzt einen der folgenden Codes verwenden, erhalten Sie immer noch 5. Wenn Sie verwenden, Usedrange.Rows.Countwas erhalten Sie? Es wird nicht 5 sein.

Hier ist ein Szenario, um zu zeigen, wie es UsedRangefunktioniert.

Geben Sie hier die Bildbeschreibung ein

xlDown ist ebenso unzuverlässig.

Betrachten Sie diesen Code

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

Was würde passieren, wenn es nur eine Zelle ( A1) gäbe, die Daten enthält? Am Ende erreichen Sie die letzte Zeile im Arbeitsblatt! Es ist so, als würde man eine Zelle auswählen A1und dann die EndTaste drücken und dann die Down ArrowTaste drücken . Dies führt auch zu unzuverlässigen Ergebnissen, wenn sich leere Zellen in einem Bereich befinden.

CountA ist auch unzuverlässig, da es zu falschen Ergebnissen führt, wenn dazwischen leere Zellen liegen.

Und daher sollte man die Verwendung von vermeiden UsedRange, xlDownund CountAdie letzte Zelle zu finden.


Finde die letzte Zeile in einer Spalte

Verwenden Sie diese Option, um die letzte Zeile in Spalte E zu finden

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

Wenn Sie bemerken, dass wir eine .vorher haben Rows.Count. Wir haben das oft ignoriert. Siehe DIESE Frage zu dem möglichen Fehler, den Sie möglicherweise erhalten. Ich rate immer .vor Rows.Countund Columns.Count. Diese Frage ist ein klassisches Szenario, in dem der Code aufgrund der Rows.CountRückgabe 65536für Excel 2003 und früher sowie 1048576für Excel 2007 und höher fehlschlägt . Ebenso Columns.Countgibt 256und 16384, respectively.

Die obige Tatsache, dass Excel 2007+ 1048576Zeilen enthält, unterstreicht auch die Tatsache, dass wir immer die Variable deklarieren sollten, die den Zeilenwert enthält, da Longstattdessen Integereine OverflowFehlermeldung angezeigt wird .

Beachten Sie, dass bei diesem Ansatz alle ausgeblendeten Zeilen übersprungen werden. Im Rückblick auf meinem Screenshot oben für Spalte A , wenn Zeile 8 versteckt waren, würde dieser Ansatz zurückkehrt 5statt 8.


Finde die letzte Reihe in einem Blatt

EffectiveVerwenden Sie diese Option, um die letzte Zeile im Blatt zu finden . Beachten Sie die Verwendung von Application.WorksheetFunction.CountA(.Cells). Dies ist erforderlich, denn wenn das Arbeitsblatt keine Zellen mit Daten enthält .Find, erhalten Sie dieseRun 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

Letzte Zeile in einer Tabelle suchen (ListObject)

Die gleichen Prinzipien gelten zum Beispiel, um die letzte Zeile in der dritten Spalte einer Tabelle zu erhalten:

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
Siddharth Rout
quelle
9
@phan: Geben Sie etwas in Zelle A5 ein. Wenn Sie nun die letzte Zeile mit einer der oben angegebenen Methoden berechnen, erhalten Sie 5. Färben Sie nun die Zelle A10 rot. Wenn Sie jetzt einen der oben genannten Codes verwenden, erhalten Sie immer noch 5. Wenn Sie verwenden, Usedrange.Rows.Countwas erhalten Sie? Es wird nicht 5 sein. Usedrange ist sehr unzuverlässig, um die letzte Zeile zu finden.
Siddharth Rout
6
Beachten Sie, dass .Find die Einstellungen des Benutzers im Dialogfeld "Suchen" leider durcheinander bringt - dh Excel verfügt nur über einen Satz von Einstellungen für das Dialogfeld, und Sie verwenden .Find ersetzt diese. Ein weiterer Trick besteht darin, UsedRange weiterhin zu verwenden, es jedoch als absolutes (aber unzuverlässiges) Maximum zu verwenden, aus dem Sie das richtige Maximum bestimmen.
Carl Colijn
4
@ CarlColijn: Ich würde es nicht als Chaos bezeichnen. :) Excel einfach remembersdie letzte Einstellung. Selbst wenn Sie manuell eine machen Find, merkt es sich die letzte Einstellung, die in der Tat ein Segen ist, wenn man diese "Tatsache" kennt
Siddharth Rout
3
@ KeithPark: Bitte mach weiter :) Wissen hat nur dann eine Bedeutung, wenn es verbreitet wird :)
Siddharth Rout
9
Ich denke, dass Ihre Beschreibung von UsedRange( es ist höchst unzuverlässig , die letzte Zelle mit Daten zu finden ) irreführend ist. UsedRangeist einfach nicht für diesen Zweck gedacht, obwohl es in einigen Fällen das richtige Ergebnis liefern kann. Ich denke, dass das vorgeschlagene Experiment die Verwirrung noch verstärkt. Das mit UsedRange($ A $ 1: $ A $ 8) erzielte Ergebnis hängt nicht davon ab, ob zuerst Daten eingegeben und gelöscht werden. Die Abbildung rechts bleibt auch ohne Eingabe und Löschung der Daten unverändert. Bitte sehen Sie meine Antwort.
sancho.s ReinstateMonicaCellio
34

Hinweis: Diese Antwort wurde durch diesen Kommentar motiviert . Der Zweck von UsedRangeunterscheidet sich von dem, was in der obigen Antwort erwähnt wird.

Um die zuletzt verwendete Zelle richtig zu finden, muss man zunächst entscheiden, was als verwendet gilt , und dann eine geeignete Methode auswählen . Ich stelle mir mindestens drei Bedeutungen vor:

  1. Verwendet = nicht leer, dh mit Daten .

  2. Used = "... in Verwendung, dh der Abschnitt, der Daten oder Formatierungen enthält ." Laut offizieller Dokumentation ist dies das Kriterium, das Excel zum Zeitpunkt des Speicherns verwendet. Siehe auch diese offizielle Dokumentation . Wenn man sich dessen nicht bewusst ist, kann das Kriterium zu unerwarteten Ergebnissen führen, aber es kann auch absichtlich ausgenutzt werden (seltener, sicher), z. B. um bestimmte Regionen hervorzuheben oder zu drucken, die möglicherweise keine Daten enthalten. Und natürlich ist es wünschenswert, dass der Bereich beim Speichern einer Arbeitsmappe als Kriterium verwendet wird, damit nicht ein Teil der Arbeit verloren geht.

  3. Used = "... in Verwendung, dh der Abschnitt, der Daten oder Formatierungen enthält " oder bedingte Formatierungen. Entspricht 2., enthält jedoch auch Zellen, die das Ziel für eine bedingte Formatierungsregel sind.

Wie Sie die zuletzt verwendete Zelle finden, hängt davon ab, was Sie möchten (Ihr Kriterium) .

Für Kriterium 1 schlage ich vor, diese Antwort zu lesen . Beachten Sie, dass dies UsedRangeals unzuverlässig bezeichnet wird. Ich denke, das ist irreführend (dh "unfair" UsedRange), da UsedRangees einfach nicht dazu gedacht ist, die letzte Zelle mit Daten zu melden. Daher sollte es in diesem Fall nicht verwendet werden, wie in dieser Antwort angegeben. Siehe auch diesen Kommentar .

Für Kriterium 2 UsedRangeist dies die zuverlässigste Option im Vergleich zu anderen Optionen, die ebenfalls für diese Verwendung entwickelt wurden. Es ist sogar unnötig, eine Arbeitsmappe zu speichern, um sicherzustellen, dass die letzte Zelle aktualisiert wird. Ctrl+ Endwird vor dem Speichern in eine falsche Zelle verschoben ("Die letzte Zelle wird erst zurückgesetzt, wenn Sie das Arbeitsblatt speichern", von http://msdn.microsoft.com/en-us/library/aa139976%28v=office.10% 29.aspx . Es ist eine alte Referenz, aber in dieser Hinsicht gültig).

Für Kriterium 3 kenne ich keine eingebaute Methode . Kriterium 2 berücksichtigt keine bedingte Formatierung. Man kann formatierte Zellen haben, basierend auf Formeln, die nicht von UsedRangeoder Ctrl+ erkannt werden End. In der Abbildung ist die letzte Zelle B3, da die Formatierung explizit darauf angewendet wurde. Zellen B6: D7 haben ein Format, das von einer bedingten Formatierungsregel abgeleitet ist, und dies wird selbst von nicht erkannt UsedRange. Um dies zu berücksichtigen, wäre eine gewisse VBA-Programmierung erforderlich.

Geben Sie hier die Bildbeschreibung ein


Zu Ihrer spezifischen Frage : Was ist der Grund dafür?

Ihr Code verwendet die erste Zelle in Ihrem Bereich E4: E48 als Trampolin, mit dem Sie nach unten springen könnenEnd(xlDown) .

Die "fehlerhafte" Ausgabe wird erhalten, wenn sich in Ihrem Bereich keine anderen als die erste möglicherweise nicht leere Zellen befinden. Dann springen Sie im Dunkeln , dh das Arbeitsblatt hinunter (Sie sollten den Unterschied zwischen leerer und leerer Zeichenfolge beachten !).

Beachten Sie, dass:

  1. Wenn Ihr Bereich nicht zusammenhängende nicht leere Zellen enthält, führt dies auch zu einem falschen Ergebnis.

  2. Wenn es nur eine nicht leere Zelle gibt, aber nicht die erste, gibt Ihnen Ihr Code immer noch das richtige Ergebnis.

sancho.s ReinstateMonicaCellio
quelle
3
Ich bin damit einverstanden, dass man zuerst entscheiden muss, was als gebraucht angesehen wird . Ich sehe mindestens 6 Bedeutungen. Die Zelle hat: 1) Daten, dh eine Formel, die möglicherweise zu einem leeren Wert führt; 2) ein Wert, dh eine nicht leere Formel oder Konstante; 3) Formatierung; 4) bedingte Formatierung; 5) eine Form (einschließlich Kommentar), die die Zelle überlappt; 6) Beteiligung an einer Tabelle (Listenobjekt). Auf welche Kombination möchten Sie testen? Einige (z. B. Tabellen) sind möglicherweise schwieriger zu testen, andere sind selten (z. B. eine Form außerhalb des Datenbereichs), andere können je nach Situation variieren (z. B. Formeln mit leeren Werten).
GlennFromIowa
20

Ich habe diese One-Stop-Funktion zum Bestimmen der letzten Zeile, Spalte und Zelle erstellt, sei es für Daten, formatierte (gruppierte / kommentierte / versteckte) Zellen oder bedingte Formatierung .

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

Die Ergebnisse sehen folgendermaßen aus:
letzte Zelle bestimmen

Für detailliertere Ergebnisse können einige Zeilen im Code unkommentiert sein:
letzte Spalte, Zeile

Es gibt eine Einschränkung: Wenn das Blatt Tabellen enthält, können die Ergebnisse unzuverlässig werden. Daher habe ich beschlossen, den Code in diesem Fall nicht auszuführen:

If .ListObjects.Count > 0 Then
    MsgBox "Cannot return reliable results, as there is at least one table in the worksheet."
    Exit Sub
End If
ZygD
quelle
2
@franklin - Ich habe gerade eine Posteingangsnachricht mit Ihrer Korrektur bemerkt, die von den Überprüfern abgelehnt wurde. Ich habe diesen Fehler korrigiert. Ich habe diese Funktion bereits einmal verwendet, als ich sie brauchte, und ich werde sie wieder verwenden, also wirklich, vielen Dank, mein Freund!
ZygD
11

Ein wichtiger Hinweis, den Sie bei der Verwendung der Lösung beachten sollten ...

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

... soll sicherstellen, dass Ihre LastRowVariable vom LongTyp ist:

Dim LastRow as Long

Andernfalls werden in bestimmten Situationen in .XLSX-Arbeitsmappen OVERFLOW-Fehler angezeigt

Dies ist meine gekapselte Funktion, die ich für verschiedene Code-Verwendungen einsetze.

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
Bischof
quelle
8

Ich würde der Antwort von Siddarth Rout hinzufügen, dass der CountA-Aufruf übersprungen werden kann, indem Find ein Range-Objekt anstelle einer Zeilennummer zurückgibt und dann das zurückgegebene Range-Objekt testet, um festzustellen, ob es Nothing ist (leeres Arbeitsblatt). .

Außerdem würde meine Version einer LastRow-Prozedur eine Null für ein leeres Arbeitsblatt zurückgeben, dann kann ich wissen, dass es leer ist.

kein comprende
quelle
8

Ich frage mich, dass niemand dies erwähnt hat, aber der einfachste Weg, die zuletzt verwendete Zelle zu erhalten, ist:

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

Dies gibt im Wesentlichen dieselbe Zelle zurück, die Sie nach Auswahl von Zelle mit Ctrl+ erhalten .EndA1

Ein Wort der Vorsicht: Excel verfolgt die Zelle unten rechts, die jemals in einem Arbeitsblatt verwendet wurde. Wenn Sie beispielsweise etwas in B3 und etwas anderes in H8 eingeben und später den Inhalt von H8 löschen , gelangen Sie durch Drücken von Ctrl+ Endimmer noch zur Zelle H8 . Die obige Funktion hat das gleiche Verhalten.

Punkt net
quelle
2
Last Cellin Excel bezieht sich manchmal auf eine leere Zelle (von Used Range), die sich von unterscheidet Last Used Cell;).
shA.t
1
Das OP brauchte nur die letzte Zeile, aber Sie haben Recht, die letzte Zelle sollte H5 sein . Aber Sie können Ihre Funktion testen, nachdem Sie den Wert in A5 gelöscht haben. Sie werden sehen, dass die letzte Zelle diese leere Zelle ist, und ich denke, Ihr Code muss so bearbeitet werden, dass Cells(1,1).Select()er möglicherweise ungültig ist ActiveSheet.Cells(1,1).Select. Auch in VBA wird die Verwendung nicht empfohlen Select;).
shA.t
5
Dies verstößt gegen zwei Grundregeln für Excel VBA: Verwenden Sie nicht Select! Und nehmen Sie nicht an, dass das gewünschte Blatt das aktive ist.
Rachel Hettinger
1
Dies ist eine alte Antwort, aber es fehlt eine Set.
BigBen
8

Da es bei der ursprünglichen Frage um Probleme beim Auffinden der letzten Zelle geht, werde ich in dieser Antwort die verschiedenen Möglichkeiten auflisten, wie Sie unerwartete Ergebnisse erzielen können. Siehe meine Antwort auf "Wie finde ich die letzte Zeile, die Daten in der Excel-Tabelle enthält, mit einem Makro?" für meine Einstellung zur Lösung dieses Problems.

Ich werde zunächst die Antwort von sancho.s und den Kommentar von GlennFromIowa erweitern und noch mehr Details hinzufügen:

[...] man muss zuerst entscheiden, was als gebraucht angesehen wird. Ich sehe mindestens 6 Bedeutungen. Zelle hat:

  • 1) Daten, dh eine Formel, die möglicherweise zu einem leeren Wert führt;
  • 2) einen Wert, dh eine nicht leere Formel oder Konstante;
  • 3) Formatierung;
  • 4) bedingte Formatierung;
  • 5) eine Form (einschließlich Kommentar), die die Zelle überlappt;
  • 6) Beteiligung an einer Tabelle (Listenobjekt).

Auf welche Kombination möchten Sie testen? Einige (z. B. Tabellen) sind möglicherweise schwieriger zu testen, andere sind selten (z. B. eine Form außerhalb des Datenbereichs), andere können je nach Situation variieren (z. B. Formeln mit leeren Werten).

Andere Dinge, die Sie vielleicht berücksichtigen möchten:

  • A) Kann es versteckte Zeilen (z. B. Autofilter), leere Zellen oder leere Zeilen geben?
  • B) Welche Art von Leistung ist akzeptabel?
  • C) Kann das VBA-Makro die Arbeitsmappe oder die Anwendungseinstellungen in irgendeiner Weise beeinflussen?

Lassen Sie uns vor diesem Hintergrund sehen, wie die gängigen Methoden zum Abrufen der "letzten Zelle" zu unerwarteten Ergebnissen führen können:

  • Der .End(xlDown)Code aus der Frage wird aus den in der Antwort von Siddharth Rout hier erläuterten Gründen am leichtesten brechen (z. B. mit einer einzelnen nicht leeren Zelle oder wenn leere Zellen dazwischen liegen ) (Suche nach "xlDown ist ebenso unzuverlässig" )
  • Jede Lösung, die auf Counting ( CountAoder Cells*.Count) basiert oder .CurrentRegionauch bei Vorhandensein leerer Zellen oder Zeilen break bricht
  • Eine Lösung, .End(xlUp)bei der vom Ende einer Spalte aus rückwärts gesucht wird, sucht ebenso wie STRG + AUF nach Daten (Formeln, die einen leeren Wert erzeugen, werden als "Daten" betrachtet) in sichtbaren Zeilen (daher kann die Verwendung mit aktiviertem Autofilter zu falschen Ergebnissen führen ⚠️) ).

    Sie müssen darauf achten, die Standard-Fallstricke zu vermeiden (für Details verweise ich hier noch einmal auf die Antwort von Siddharth Rout , siehe Abschnitt "Letzte Zeile in einer Spalte suchen " ), z. B. das Hardcodieren der letzten Zeile ( Range("A65536").End(xlUp)) anstatt sich darauf zu verlassen sht.Rows.Count.

  • .SpecialCells(xlLastCell)entspricht STRG + ENDE und gibt die Zelle ganz unten und ganz rechts des "verwendeten Bereichs" zurück. Daher gelten alle Einschränkungen, die für die Verwendung des "verwendeten Bereichs" gelten, auch für diese Methode. Darüber hinaus wird der "verwendete Bereich" nur beim Speichern der Arbeitsmappe und beim Zugriff zurückgesetzt worksheet.UsedRange, sodass xlLastCellmöglicherweise veraltete Ergebnisse mit nicht gespeicherten Änderungen auftreten (z. B. nachdem einige Zeilen gelöscht wurden). Siehe die Antwort in der Nähe von dotNET .
  • sht.UsedRange(ausführlich beschrieben in der Antwort von sancho.s hier) berücksichtigt sowohl Daten als auch Formatierungen (wenn auch keine bedingte Formatierung) und setzt den "verwendeten Bereich" des Arbeitsblatts zurück , der möglicherweise Ihren Wünschen entspricht oder nicht.

    Beachten Sie, dass ein häufiger Fehler bei der Verwendung von .UsedRange.Rows.Count⚠️ ist, bei dem die Anzahl der Zeilen im verwendeten Bereich und nicht die letzte Zeilennummer zurückgegeben wird (sie unterscheiden sich, wenn die ersten Zeilen leer sind). Weitere Informationen finden Sie in der Antwort von newguy auf Wie finde ich ? letzte Zeile, die Daten in der Excel-Tabelle mit einem Makro enthält?

  • .FindMit dieser Option können Sie die letzte Zeile mit beliebigen Daten (einschließlich Formeln) oder einem nicht leeren Wert in einer beliebigen Spalte finden . Sie können wählen, ob Sie an Formeln oder Werten interessiert sind. Der Haken dabei ist jedoch, dass die Standardeinstellungen im Excel-Dialogfeld "Suchen" zurückgesetzt werden, was für Ihre Benutzer sehr verwirrend sein kann. Es muss auch sorgfältig verwendet werden, siehe die Antwort von Siddharth Rout hier (Abschnitt "Letzte Zeile in einem Blatt finden" )
  • Explizitere Lösungen, die einzelne Personen Cellsin einer Schleife prüfen , sind im Allgemeinen langsamer als die Wiederverwendung einer Excel-Funktion (obwohl sie immer noch performant sein können). Sie können jedoch genau angeben, was Sie suchen möchten. In meiner Lösung basierend auf UsedRangeund VBA-Arrays finden Sie die letzte Zelle mit Daten in der angegebenen Spalte. Sie verarbeitet ausgeblendete Zeilen, Filter und Leerzeichen, ändert die Standardeinstellungen für Suchen nicht und ist recht leistungsfähig.

Egal für welche Lösung Sie sich entscheiden, seien Sie vorsichtig

  • zu verwenden, Longanstatt Integerdie Zeilennummern zu speichern (um zu vermeiden, dass Overflowmehr als 65.000 Zeilen verwendet werden) und
  • um immer das Arbeitsblatt anzugeben, mit dem Sie arbeiten (dh Dim ws As Worksheet ... ws.Range(...)anstelle von Range(...))
  • Vermeiden Sie bei der Verwendung .Value(was a ist Variant) implizite Umwandlungen, .Value <> ""da diese fehlschlagen, wenn die Zelle einen Fehlerwert enthält.
Nickolay
quelle
4

Diese Frage versucht jedoch, die letzte Zeile mit VBA zu finden. Ich denke, es wäre gut, eine Array-Formel für die Arbeitsblattfunktion aufzunehmen, da diese häufig besucht wird:

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

Sie müssen die Formel ohne Klammern eingeben und dann Shift+ Ctrl+ drücken Enter, um sie zu einer Array-Formel zu machen.

Dies gibt Ihnen die Adresse der zuletzt verwendeten Zelle in der Spalte D.

M--
quelle
1
Ich mag das. Ich könnte etwas ändern, um nur die Zeilennummer zu erhalten ... '{= MATCH (INDEX (D: D, MAX (WENN (D: D <> "", REIHE (D: D) -ROW (D1) +1)) , 1), D: D, 0)} '
PGSystemTester
3
sub last_filled_cell()
msgbox range("A65536").end(xlup).row
end sub

Hier A65536ist die letzte Zelle in Spalte A, in der dieser Code in Excel 2003 getestet wurde.

Ashwith Ullal
quelle
Können Sie erklären, wie Ihr Code diese alte Frage beantwortet?
Shoover
1
Obwohl diese Antwort wahrscheinlich richtig und nützlich ist, wird sie bevorzugt, wenn Sie eine Erklärung hinzufügen, um zu erklären, wie sie zur Lösung des Problems beiträgt. Dies ist besonders in Zukunft nützlich, wenn eine Änderung (möglicherweise ohne Bezug) dazu führt, dass sie nicht mehr funktioniert und die Benutzer verstehen müssen, wie sie einmal funktioniert hat.
Kevin Brown
2

Ich war auf der Suche nach einem Weg zu imitieren die CTRL+ Shift+ End, so dotNET Lösung groß ist, es sei denn mit meiner Excel 2010 Ich brauche ein hinzufügen , setwenn ich einen Fehler vermeiden wollen:

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

und wie Sie dies selbst überprüfen können:

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
J. Chomel
quelle
1

Hier sind meine zwei Cent.

Meiner Meinung nach ist das Risiko, dass eine versteckte Zeile mit Daten ausgeschlossen wird, zu hoch, um xlUpals One-Stop-Antwort angesehen zu werden . Ich bin damit einverstanden, dass es einfach ist und die meiste Zeit funktioniert, aber es birgt das Risiko, die letzte Zeile ohne Vorwarnung zu unterschätzen. Dies könnte zu CATASTROPHIC- Ergebnissen an einem bestimmten Punkt für jemanden führen, der auf Stack Overlow gesprungen ist und nach einem "sicheren Weg" gesucht hat, um diesen Wert zu erfassen.

Die FindMethode ist einwandfrei und ich würde sie als One-Stop-Antwort billigen . Der Nachteil des Änderns der FindEinstellungen kann jedoch ärgerlich sein, insbesondere wenn dies Teil einer UDF ist.

Die anderen Antworten sind in Ordnung, die Komplexität wird jedoch etwas übertrieben. Hier ist mein Versuch, ein Gleichgewicht zwischen Zuverlässigkeit, minimaler Komplexität und Nichtverwendung zu finden 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

Warum das gut ist:

  • Ziemlich einfach, nicht viele Variablen.
  • Ermöglicht mehrere Spalten.
  • Ändert keine FindEinstellungen
  • Dynamisch, wenn als UDF verwendet, wobei die gesamte Spalte ausgewählt ist.

Warum das so schlecht ist:

  • Bei sehr großen Datenmengen und einer großen Lücke zwischen dem verwendeten Bereich und der letzten Zeile in den angegebenen Spalten ist die Leistung langsamer, in seltenen Fällen erheblich langsamer.

Ich denke jedoch, dass eine One-Stop-Lösung, die den Nachteil hat, findEinstellungen zu verfälschen oder langsamer zu arbeiten, eine bessere Gesamtlösung ist. Ein Benutzer kann dann an seinen Einstellungen basteln, um zu versuchen, sie zu verbessern, und weiß, was mit seinem Code los ist. Die Verwendung xLUpwarnt nicht vor potenziellen Risiken und sie könnten so lange weitermachen, wer weiß, wie lange sie nicht wissen, dass ihr Code nicht richtig funktioniert.

PGSystemTester
quelle
1

In den letzten 3+ Jahren sind dies die Funktionen, mit denen ich die letzte Zeile und die letzte Spalte pro definierter Spalte (für Zeile) und Zeile (für Spalte) finde:

Letzte Spalte:

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

Letzte Reihe:

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

Für den Fall des OP ist dies der Weg, um die letzte Zeile in der Spalte zu erhalten E:

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

Letzte Zeile, leere Zeilen mit Daten zählen:

Hier können wir die bekannten Excel-Formeln verwenden , die uns die letzte Zeile eines Arbeitsblatts in Excel geben, ohne VBA einzubeziehen -=IFERROR(LOOKUP(2,1/(NOT(ISBLANK(A:A))),ROW(A:A)),0)

Um dies in VBA zu speichern und nichts in Excel zu schreiben, wobei die Parameter für die letzteren Funktionen verwendet werden, könnte Folgendes in Betracht gezogen werden:

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
Vityata
quelle
Dies gibt ein falsches Ergebnis zurück, wenn die letzte Zeile / Spalte ausgeblendet ist.
PGSystemTester
@PGSystemTester - ja, aber nach meinem Verständnis ist es nicht die letzte Spalte / Zeile, die benötigt wird, wenn ich es programmiere, wenn es ausgeblendet ist.
Vityata
Schön, dass das bei dir funktioniert. Ich vermute, Ihre Situation ist kein typischer Anwendungsfall. Wenn ich mit Clients arbeite, die die letzte Zeile benötigen, suchen sie häufiger nach der niedrigsten Zelle mit Daten, nicht nach der niedrigsten sichtbaren Zelle mit Daten. Wie auch immer ... ich bin froh, dass es funktioniert. 👍
PGSystemTester
@PGSystemTester - Ich habe verstanden, aber sich um die Struktur zu kümmern und keine unsichtbaren Zellen zuzulassen, funktioniert wie ein Zauber.
Vityata
@PGSystemTester - Ja, wenn die Aufgabe möglicherweise leere Zeilen zulässt, würde ich wahrscheinlich die EVAL()und die berühmte Excel-Formel verwenden. Obwohl die Leute vielleicht denken, dass Eval()das böse ist und dies eine weitere interessante Geschichte ist, über die man schreiben kann ...
Vityata
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
user85489
quelle