Error al encontrar la última celda utilizada en Excel con VBA


179

Cuando quiero encontrar el último valor de celda utilizado, uso:

Dim LastRow As Long

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

Debug.Print LastRow

Estoy obteniendo el resultado incorrecto cuando pongo un solo elemento en una celda. Pero cuando pongo más de un valor en la celda, la salida es correcta. ¿Cuál es la razón detrás de esto?


Respuestas:


309

NOTA : Tengo la intención de hacer de esto una "publicación única" en la que pueda usar la Correctforma de encontrar la última fila. Esto también cubrirá las mejores prácticas a seguir al encontrar la última fila. Y, por lo tanto, seguiré actualizándolo cada vez que encuentre un nuevo escenario / información.


Formas poco confiables de encontrar la última fila

Algunas de las formas más comunes de encontrar la última fila son muy poco confiables y, por lo tanto, nunca deben usarse.

  1. Rango utilizado
  2. xlDown
  3. CountA

UsedRangedebe NUNCA ser utilizado para encontrar la última celda que tiene datos. Es muy poco confiable. Prueba este experimento.

Escribe algo en la celda A5. Ahora, cuando calcules la última fila con cualquiera de los métodos que figuran a continuación, te dará 5. Ahora colorea la celda de A10rojo. Si ahora usa cualquiera de los siguientes códigos, todavía obtendrá 5. Si usa, Usedrange.Rows.Count¿qué obtiene? No serán las 5.

Aquí hay un escenario para mostrar cómo UsedRangefunciona.

ingrese la descripción de la imagen aquí

xlDown Es igualmente poco confiable.

Considera este código

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

¿Qué pasaría si solo hubiera una celda ( A1) que tuviera datos? ¡Terminarás llegando a la última fila de la hoja de trabajo! Es como seleccionar la celda A1y luego presionar la Endtecla y luego presionar la Down Arrowtecla. Esto también le dará resultados poco confiables si hay celdas en blanco en un rango.

CountA tampoco es confiable porque le dará un resultado incorrecto si hay celdas en blanco en el medio.

Y por lo tanto, uno debe evitar el uso de UsedRange, xlDowny CountAencontrar la última celda.


Encuentra la última fila en una columna

Para encontrar la última fila en la columna E, use esto

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

Si notas que tenemos un .antes Rows.Count. A menudo elegimos ignorar eso. Vea ESTA pregunta sobre el posible error que puede obtener. Siempre aconsejo usar .antes Rows.County Columns.Count. Esa pregunta es un escenario clásico donde el código fallará porque los Rows.Countretornos 65536para Excel 2003 y anteriores y 1048576para Excel 2007 y posteriores. Del mismo modo Columns.Countvuelve 256y 16384, respectivamente.

El hecho anterior de que Excel 2007+ tiene 1048576filas también enfatiza el hecho de que siempre debemos declarar la variable que contendrá el valor de la fila, ya que en Longlugar de Integereso obtendrá un Overflowerror.

Tenga en cuenta que este enfoque omitirá las filas ocultas. Mirando hacia atrás en mi captura de pantalla anterior para la columna A , si la fila 8 estuviera oculta, este enfoque volvería en 5lugar de hacerlo 8.


Encuentra la última fila en una hoja

Para encontrar la Effectiveúltima fila en la hoja, use esto. Observe el uso de Application.WorksheetFunction.CountA(.Cells). Esto es necesario porque si no hay celdas con datos en la hoja de trabajo, .Findle dará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

Buscar la última fila en una tabla (ListObject)

Se aplican los mismos principios, por ejemplo, para obtener la última fila en la tercera columna de una tabla:

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: Escriba algo en la celda A5. Ahora, cuando calcules la última fila con cualquiera de los métodos anteriores, te dará 5. Ahora colorea la celda A10 de rojo. Si ahora usa cualquiera de los códigos anteriores, aún obtendrá 5. Si usa, Usedrange.Rows.Count¿qué obtiene? No será 5. El rango utilizado no es muy confiable para encontrar la última fila.
Siddharth Rout

66
Tenga en cuenta que .Find desafortunadamente desordena la configuración del usuario en el cuadro de diálogo Buscar, es decir, Excel solo tiene 1 conjunto de configuraciones para el cuadro de diálogo, y el uso de .Find las reemplaza. Otro truco es seguir utilizando UsedRange, pero utilícelo como un máximo absoluto (pero no confiable) a partir del cual determine el máximo correcto.
Carl Colijn

44
@CarlColijn: No lo llamaría desordenado. :) Excel simplemente remembersla última configuración. Incluso cuando realiza manualmente un Find, recuerda la última configuración, que de hecho es una bendición si uno conoce este "hecho"
Siddharth Rout

3
@KeithPark: Adelante :) El conocimiento solo tiene un significado si se difunde :)
Siddharth Rout

9
Creo que su descripción de UsedRange( es muy poco confiable encontrar la última celda que tiene datos ) es engañosa. UsedRangesimplemente no está destinado para ese propósito, aunque en algunos casos puede dar el resultado correcto. Creo que el experimento propuesto se suma a la confusión. El resultado obtenido con UsedRange($ A $ 1: $ A $ 8) no depende de ingresar primero los datos y eliminarlos. La figura de la derecha seguirá siendo la misma incluso sin haber ingresado datos y eliminado. Por favor mira mi respuesta.
sancho.s ReinstateMonicaCellio

34

Nota: esta respuesta fue motivada por este comentario . El propósito de UsedRangees diferente de lo que se menciona en la respuesta anterior.

En cuanto a la forma correcta de encontrar la última celda utilizada, primero hay que decidir qué se considera utilizado y luego seleccionar un método adecuado . Concibo al menos tres significados:

  1. Usado = no en blanco, es decir, que tiene datos .

  2. Used = "... en uso, es decir, la sección que contiene datos o formato ". Según la documentación oficial , este es el criterio utilizado por Excel al momento de guardar. Consulte también esta documentación oficial . Si uno no es consciente de esto, el criterio puede producir resultados inesperados, pero también puede explotarse intencionalmente (con menos frecuencia, seguramente), por ejemplo, para resaltar o imprimir regiones específicas, que eventualmente pueden no tener datos. Y, por supuesto, es deseable como criterio para el rango a utilizar al guardar un libro de trabajo, para no perder parte del trabajo de uno.

  3. Usado = "... en uso, es decir, la sección que contiene datos o formato " o formato condicional. Igual que 2., pero también incluye celdas que son el objetivo de cualquier regla de Formato condicional.

Cómo encontrar la última celda utilizada depende de lo que desee (su criterio) .

Para el criterio 1, sugiero leer esta respuesta . Tenga en cuenta que UsedRangese cita como poco confiable. Creo que eso es engañoso (es decir, "injusto" UsedRange), ya UsedRangeque simplemente no está destinado a informar la última celda que contiene datos. Por lo tanto, no debe usarse en este caso, como se indica en esa respuesta. Ver también este comentario .

Para el criterio 2, UsedRangees la opción más confiable , en comparación con otras opciones también diseñadas para este uso. Incluso hace innecesario guardar un libro de trabajo para asegurarse de que se actualice la última celda. Ctrl+ Endirá a una celda incorrecta antes de guardar ("La última celda no se restablece hasta que guarde la hoja de trabajo", de http://msdn.microsoft.com/en-us/library/aa139976%28v=office.10% 29.aspx . Es una referencia antigua, pero a este respecto es válida).

Para el criterio 3, no conozco ningún método incorporado . El criterio 2 no tiene en cuenta el formato condicional. Uno puede tener celdas formateadas, basadas en fórmulas, que no son detectadas por UsedRangeo Ctrl+ End. En la figura, la última celda es B3, ya que el formato se le aplicó explícitamente. Las celdas B6: D7 tienen un formato derivado de una regla de Formato condicional, y esto ni siquiera lo detecta UsedRange. Tener en cuenta esto requeriría cierta programación de VBA.

ingrese la descripción de la imagen aquí


En cuanto a su pregunta específica : ¿Cuál es la razón detrás de esto?

Su código usa la primera celda en su rango E4: E48 como un trampolín, para saltar con End(xlDown).

La salida "errónea" se obtendrá si no hay celdas no vacías en su rango que no sean las primeras. Luego, estás saltando en la oscuridad , es decir, hacia abajo en la hoja de trabajo (¡debes notar la diferencia entre una cadena en blanco y vacía !).

Tenga en cuenta que:

  1. Si su rango contiene celdas no en blanco no contiguas, también dará un resultado incorrecto.

  2. Si solo hay una celda no en blanco, pero no es la primera, su código aún le dará el resultado correcto.


3
Estoy de acuerdo en que primero hay que decidir qué se considera usado . Veo al menos 6 significados. La celda tiene: 1) datos, es decir, una fórmula, que posiblemente resulte en un valor en blanco; 2) un valor, es decir, una fórmula no constante o constante; 3) formateo; 4) formato condicional; 5) una forma (incluido Comentario) que se superpone a la celda; 6) participación en una tabla (objeto de lista). ¿Qué combinación quieres probar? Algunos (como las Tablas) pueden ser más difíciles de probar, y algunos pueden ser raros (como una forma fuera del rango de datos), pero otros pueden variar según la situación (por ejemplo, fórmulas con valores en blanco).
GlennFromIowa

20

Creé esta función integral para determinar la última fila, columna y celda, ya sea para datos, celdas formateadas (agrupadas / comentadas / ocultas) o formato condicional .

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

Los resultados se ven así:
determinar la última celda

Para resultados más detallados, algunas líneas en el código pueden ser descomentadas:
última columna, fila

Existe una limitación: si hay tablas en la hoja, los resultados pueden volverse poco confiables, por lo que decidí evitar ejecutar el código en este caso:

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: acabo de notar un mensaje de la bandeja de entrada con su corrección que fue rechazado por los revisores. Corrigí ese error. Ya usé esta función una vez cuando la necesitaba y la volveré a usar, así que realmente, muchas gracias, mi amigo.
ZygD

11

Una nota importante a tener en cuenta al usar la solución ...

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

... es asegurarse de que su LastRowvariable sea de Longtipo:

Dim LastRow as Long

De lo contrario, terminará recibiendo errores de DESBORDAMIENTO en ciertas situaciones en los libros de trabajo .XLSX

Esta es mi función encapsulada que uso en varios usos de código.

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

Agregaría a la respuesta dada por Siddarth Rout para decir que la llamada CountA se puede omitir haciendo que Find devuelva un objeto Range, en lugar de un número de fila, y luego pruebe el objeto Range devuelto para ver si es Nothing (hoja de trabajo en blanco) .

Además, me gustaría que mi versión de cualquier procedimiento de LastRow devuelva un cero para una hoja de trabajo en blanco, entonces puedo saber que está en blanco.


8

Me pregunto si nadie ha mencionado esto, pero la forma más fácil de obtener la última celda utilizada es:

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

Esto esencialmente devuelve la misma celda que obtiene por Ctrl+ Enddespués de seleccionar Celda A1.

Una advertencia: Excel realiza un seguimiento de la celda más inferior derecha que se haya utilizado en una hoja de trabajo. Entonces, si por ejemplo ingresas algo en B3 y algo más en H8 y luego eliminas el contenido de H8 , presionar Ctrl+ Endaún te llevará a la celda H8 . La función anterior tendrá el mismo comportamiento.


2
Last Cellen Excel a veces se refiere a una celda vacía (desde Used Range) que es diferente de Last Used Cell;).
shA.t

1
El OP necesitaba solo la última fila, pero tiene razón, la última celda debe ser H5 ; Pero usted puede probar su función después de la eliminación de valor en la A5 Usted verá que la última celda que es celda vacía, y creo que el código necesita algunas modificaciones como que Cells(1,1).Select()no es válido es que tal vez ActiveSheet.Cells(1,1).Select; También en VBA no se recomienda su uso Select;).
shA.t

55
Esto rompe dos reglas cardinales para Excel VBA: ¡No use Seleccionar! Y no asuma que la hoja que desea es la activa.
Rachel Hettinger

1
Esta es una vieja respuesta, pero le falta a Set.
BigBen

8

Dado que la pregunta original es sobre problemas para encontrar la última celda, en esta respuesta enumeraré las diversas formas en que puede obtener resultados inesperados ; vea mi respuesta a "¿Cómo puedo encontrar la última fila que contiene datos en la hoja de Excel con una macro?" por mi opinión sobre resolver esto.

Comenzaré ampliando la respuesta de sancho.s y el comentario de GlennFromIowa , agregando aún más detalles:

[...] primero hay que decidir qué se considera usado. Veo al menos 6 significados. La celda tiene:

  • 1) datos, es decir, una fórmula, posiblemente resultando en un valor en blanco;
  • 2) un valor, es decir, una fórmula no constante o constante;
  • 3) formateo;
  • 4) formato condicional;
  • 5) una forma (incluido Comentario) que se superpone a la celda;
  • 6) participación en una tabla (objeto de lista).

¿Qué combinación quieres probar? Algunos (como las Tablas) pueden ser más difíciles de probar, y algunos pueden ser raros (como una forma fuera del rango de datos), pero otros pueden variar según la situación (por ejemplo, fórmulas con valores en blanco).

Otras cosas que puede considerar:

  • A) ¿Puede haber filas ocultas (por ejemplo, autofiltro), celdas en blanco o filas en blanco?
  • B) ¿Qué tipo de rendimiento es aceptable?
  • C) ¿Puede la macro VBA afectar el libro de trabajo o la configuración de la aplicación de alguna manera?

Con eso en mente, veamos cómo las formas comunes de obtener la "última celda" pueden producir resultados inesperados:

  • El .End(xlDown)código de la pregunta se romperá más fácilmente (por ejemplo, con una sola celda no vacía o cuando hay celdas en blanco en el medio ) por las razones explicadas en la respuesta de Siddharth Rout aquí (buscar "xlDown es igualmente poco confiable". ) 👎
  • Cualquier solución basada en Counting ( CountAo Cells*.Count) o.CurrentRegion también se romperá en presencia de celdas o filas en blanco 👎
  • Una solución que implica .End(xlUp)buscar hacia atrás desde el final de una columna, al igual que CTRL + ARRIBA, buscará datos (las fórmulas que producen un valor en blanco se consideran "datos") en filas visibles (por lo que usarlo con el autofiltro habilitado puede producir resultados incorrectos ⚠️ )

    Debe evitar las trampas estándar (para obtener más detalles, volveré a consultar la respuesta de Siddharth Rout aquí, busque la sección "Buscar la última fila en una columna" ), como codificar la última fila ( Range("A65536").End(xlUp)) en lugar de depender sht.Rows.Count.

  • .SpecialCells(xlLastCell)es equivalente a CTRL + FIN, devolviendo la celda más inferior y más a la derecha del "rango usado", por lo que todas las advertencias que se aplican a confiar en el "rango usado", también se aplican a este método. Además, el "rango utilizado" solo se restablece al guardar el libro de trabajo y al acceder worksheet.UsedRange, por lo que xlLastCellpuede producir resultados obsoletos⚠️ con modificaciones no guardadas (por ejemplo, después de que se eliminan algunas filas). Vea la respuesta cercana de dotNET .
  • sht.UsedRange(descrito en detalle en la respuesta de sancho.s aquí) considera tanto los datos como el formato (aunque no el formato condicional) y restablece el "rango utilizado" de la hoja de trabajo , que puede o no ser lo que desea.

    Tenga en cuenta que un error común ️ es usar .UsedRange.Rows.Count⚠️, que devuelve el número de filas en el rango utilizado, no el número de la última fila (serán diferentes si las primeras filas están en blanco), para más detalles vea la respuesta de Newguy a ¿Cómo puedo encontrar? última fila que contiene datos en la hoja de Excel con una macro?

  • .Findle permite encontrar la última fila con cualquier dato (incluidas las fórmulas) o un valor que no esté en blanco en cualquier columna . Puede elegir si le interesan las fórmulas o los valores, pero el problema es que restablece los valores predeterminados en el cuadro de diálogo Buscar de Excel ️️⚠️, lo que puede ser muy confuso para sus usuarios. También debe usarse con cuidado, vea la respuesta de Siddharth Rout aquí (sección "Buscar la última fila en una hoja" )
  • Las soluciones más explícitas que comprueban individualmente Cellsen un bucle generalmente son más lentas que volver a usar una función de Excel (aunque todavía puede ser eficaz), pero le permiten especificar exactamente lo que desea encontrar. Vea mi solución basada en UsedRangematrices VBA para encontrar la última celda con datos en la columna dada: maneja filas ocultas, filtros, espacios en blanco, no modifica los valores predeterminados de búsqueda y es bastante eficiente.

Sea cual sea la solución que elija, tenga cuidado

  • para usar en Longlugar de Integeralmacenar los números de fila (para evitar obtenerOverflow más de 65k filas) y
  • para especificar siempre la hoja de trabajo con la que está trabajando (es decir, en Dim ws As Worksheet ... ws.Range(...)lugar deRange(...) )
  • al usar .Value(que es a Variant) evite conversiones implícitas, .Value <> ""ya que fallarán si la celda contiene un valor de error.

4

Sin embargo, esta pregunta busca encontrar la última fila usando VBA, creo que sería bueno incluir una fórmula de matriz para la función de la hoja de trabajo, ya que esta se visita con frecuencia:

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

Debe ingresar la fórmula sin corchetes y luego presionar Shift+ Ctrl+Enter para convertirla en una fórmula de matriz.

Esto le dará la dirección de la última celda utilizada en la columna D.


1
Me gusta esto. Podría alterar ligeramente para obtener solo el número de fila ... '{= 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

Aquí, A65536está la última celda en la Columna A, este código fue probado en Excel 2003.


¿Puede explicar cómo su código responde a esta vieja pregunta?
Shoover 01 de

1
Si bien esta respuesta es probablemente correcta y útil, se prefiere si incluye alguna explicación junto con ella para explicar cómo ayuda a resolver el problema. Esto se vuelve especialmente útil en el futuro, si hay un cambio (posiblemente no relacionado) que hace que deje de funcionar y los usuarios necesitan entender cómo funcionó una vez.
Kevin Brown

2

Estaba buscando una manera de imitar el CTRL+ Shift+ End, por lo que la solución dotNET es excelente, excepto que con mi Excel 2010 necesito agregar un setsi quiero evitar un error:

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

y cómo comprobar esto por ti mismo:

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

Aquí están mis dos centavos.

En mi humilde opinión, el riesgo de una fila oculta con la exclusión de datos es demasiado importante como para dejarlo xlUpcomo una respuesta integral . Estoy de acuerdo en que es simple y funcionará la mayor parte del tiempo, pero presenta el riesgo de subestimar la última fila, sin previo aviso. Esto podría producir CATASTROPHIC resultados en algún momento para alguien que saltó a Stack Overlow y estaba buscando "una forma segura" de capturar este valor.

El Findmétodo es perfecto y lo aprobaría como una respuesta única . Sin embargo, el inconveniente de cambiar la Findconfiguración puede ser molesto, especialmente si es parte de un UDF.

Las otras respuestas publicadas están bien, sin embargo, la complejidad se vuelve un poco excesiva. Por lo tanto, aquí está mi intento de encontrar un equilibrio de confiabilidad, complejidad mínima y no usar 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

Por qué esto es bueno:

  • Razonablemente simple, no hay muchas variables.
  • Permite múltiples columnas.
  • No modifica la Findconfiguración
  • Dinámico si se usa como UDF con toda la columna seleccionada.

Por qué esto es malo:

  • Con conjuntos de datos muy grandes y una brecha masiva entre el rango utilizado y la última fila en columnas especificadas, esto funcionará más lentamente, en casos excepcionales SIGNIFICATIVAMENTE más lento.

Sin embargo, creo que una findsolución integral que tiene el inconveniente de estropear la configuración o funcionar más lentamente es una mejor solución general. Un usuario puede luego modificar su configuración para intentar mejorar, sabiendo lo que está sucediendo con su código. El uso xLUpno advertirá sobre los riesgos potenciales y podrían continuar por quién sabe cuánto tiempo sin saber que su código no funcionaba correctamente.


1

Durante los últimos 3 años, estas son las funciones que estoy usando para encontrar la última fila y la última columna por columna definida (para la fila) y fila (para la columna):

Última columna:

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

Última fila:

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

Para el caso del OP, esta es la forma de obtener la última fila en la columna E:

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

Última fila, contando filas vacías con datos:

Aquí podemos usar las conocidas fórmulas de Excel , que nos dan la última fila de una hoja de cálculo en Excel, sin involucrar a VBA:=IFERROR(LOOKUP(2,1/(NOT(ISBLANK(A:A))),ROW(A:A)),0)

Para poner esto en VBA y no escribir nada en Excel, usando los parámetros para las últimas funciones, algo como esto podría estar en mente:

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

Esto devolverá un resultado incorrecto si la última fila / columna está oculta.
PGSystemTester

@PGSystemTester: sí, pero según tengo entendido, cuando lo programo, si está oculto, no se necesita la última columna / fila.
Vityata

Me alegro de que te funcione. Sospecho que su situación no es un caso de uso típico. Con mayor frecuencia cuando trabajo con clientes que necesitan la última fila, buscan la celda más baja con datos, no la celda visible más baja con datos. De todos modos ... me alegro de que funcione. 👍
PGSystemTester

@PGSystemTester: entendí tu punto, pero cuidar la estructura y no permitir que las celdas invisibles funcionen de maravilla.
Vityata

@PGSystemTester: sí, si la tarea posiblemente permite filas vacías, probablemente usaría EVAL()la famosa fórmula de Excel. Aunque la gente puede pensar que Eval()es malo y esta es otra historia interesante sobre la que escribir ...
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
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.