Erro ao localizar a última célula usada no Excel com VBA

179

Quando eu quero encontrar o último valor de célula usado, eu uso:

Dim LastRow As Long

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

Debug.Print LastRow

Estou obtendo a saída errada quando coloco um único elemento em uma célula. Mas quando eu coloco mais de um valor na célula, a saída está correta. Qual a razão por trás disso?

James
fonte

Respostas:

309

NOTA : Pretendo fazer deste um "posto único" onde você possa usar o Correctcaminho para encontrar a última linha. Isso também abordará as práticas recomendadas a serem seguidas ao encontrar a última linha. E, portanto, continuarei atualizando sempre que encontrar um novo cenário / informação.


Maneiras não confiáveis ​​de encontrar a última linha

Algumas das maneiras mais comuns de encontrar a última linha que não são confiáveis ​​e, portanto, nunca devem ser usadas.

  1. UsedRange
  2. xlDown
  3. CountA

UsedRangeNUNCA deve ser usado para encontrar a última célula que possui dados. É altamente não confiável. Tente este experimento.

Digite algo na célula A5. Agora, quando você calcula a última linha com qualquer um dos métodos fornecidos abaixo, ele fornecerá 5. Agora, pinte a célula de A10vermelho. Se você agora usa qualquer um dos códigos abaixo, ainda receberá 5. Se você usar Usedrange.Rows.Counto que recebe? Não será 5.

Aqui está um cenário para mostrar como UsedRangefunciona.

insira a descrição da imagem aqui

xlDown é igualmente não confiável.

Considere este código

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

O que aconteceria se houvesse apenas uma célula ( A1) com dados? Você chegará à última linha da planilha! É como selecionar uma célula A1e pressionar a Endtecla e pressionar a Down Arrowtecla. Isso também fornecerá resultados não confiáveis ​​se houver células em branco em um intervalo.

CountA também não é confiável porque fornecerá resultados incorretos se houver células em branco no meio.

E, portanto, deve-se evitar o uso de UsedRange, xlDowne CountApara encontrar a última célula.


Localizar última linha em uma coluna

Para encontrar a última linha na Col E, use este

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

Se você perceber que temos um .antes Rows.Count. Muitas vezes escolhemos ignorar isso. Veja ESTA pergunta sobre o possível erro que você pode receber. Eu sempre aconselho usar .antes Rows.Counte Columns.Count. Essa pergunta é um cenário clássico em que o código falhará porque Rows.Countretorna 65536para o Excel 2003 e versões anteriores e 1048576para o Excel 2007 e versões posteriores. Da mesma forma Columns.Countretorna 256e 16384, respectivamente.

O fato acima que o Excel 2007+ possui 1048576linhas também enfatiza o fato de que sempre devemos declarar a variável que manterá o valor da linha, pois, em Longvez disso Integer, você receberá um Overflowerro.

Observe que essa abordagem ignorará todas as linhas ocultas. Olhando para a minha captura de tela acima da coluna A , se a linha 8 estivesse oculta, essa abordagem retornaria em 5vez de 8.


Localizar última linha em uma planilha

Para encontrar a Effectiveúltima linha na planilha, use isto. Observe o uso de Application.WorksheetFunction.CountA(.Cells). Isso é necessário porque, se não houver células com dados na planilha, .Findvocê fornecerá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

Localizar última linha em uma tabela (ListObject)

Os mesmos princípios se aplicam, por exemplo, para obter a última linha na terceira coluna de uma tabela:

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
fonte
9
@phan: Digite algo na célula A5. Agora, quando você calcula a última linha com qualquer um dos métodos fornecidos acima, ele fornece 5. Agora, pinte a célula A10 de vermelho. Se agora você usar qualquer um dos códigos acima, ainda receberá 5. Se você usar Usedrange.Rows.Counto que obtém? Não será 5. Usedrange é altamente confiável para encontrar a última linha.
Siddharth Rout
6
Observe que .Find, infelizmente, atrapalha as configurações do usuário na caixa de diálogo Localizar - ou seja, o Excel possui apenas 1 conjunto de configurações para a caixa de diálogo e o uso de .Find as substitui. Outro truque é ainda usar UsedRange, mas usá-lo como um máximo absoluto (mas não confiável) a partir do qual você determina o máximo correto.
Carl Colijn
4
@CarlColijn: Eu não chamaria isso de bagunça. :) Excel simplesmente remembersa última configuração. Mesmo quando você faz manualmente um Find, ele se lembra a última definição que na verdade é uma benção quando se sabe este "fato"
Siddharth Rout
3
@KeithPark: Por favor, vá em frente :) Conhecimento só tem significado se for espalhado :) #
Siddharth Rout
9
Eu acho que sua descrição de UsedRange( é altamente confiável encontrar a última célula que possui dados ) é enganosa. UsedRangesimplesmente não se destina a esse fim, mesmo que em alguns casos possa dar o resultado correto. Eu acho que o experimento proposto aumenta a confusão. O resultado obtido com UsedRange($ A $ 1: $ A $ 8) não depende da entrada dos dados e da exclusão deles. A figura à direita ainda será a mesma, mesmo sem ter inserido os dados e excluído. Por favor, veja minha resposta.
sancho.s ReinstateMonicaCellio
34

Nota: esta resposta foi motivada por este comentário . O objetivo de UsedRangeé diferente do mencionado na resposta acima.

Quanto à maneira correta de encontrar a última célula usada, é preciso primeiro decidir o que é considerado usado e depois selecionar um método adequado . Concebo pelo menos três significados:

  1. Usado = não em branco, ou seja, com dados .

  2. Used = "... em uso, significando a seção que contém dados ou formatação ." De acordo com a documentação oficial , este é o critério usado pelo Excel no momento da economia. Veja também esta documentação oficial . Se não estiver ciente disso, o critério pode produzir resultados inesperados, mas também pode ser explorado intencionalmente (com menos frequência, com certeza), por exemplo, para destacar ou imprimir regiões específicas, que podem eventualmente não ter dados. E, é claro, é desejável como critério o intervalo a ser usado ao salvar uma pasta de trabalho, para não perder parte do trabalho.

  3. Usado = "... em uso, significando a seção que contém dados ou formatação " ou formatação condicional. O mesmo que 2., mas também incluindo células que são o destino de qualquer regra de Formatação Condicional.

Como encontrar a última célula usada depende do que você deseja (seu critério) .

Para o critério 1, sugiro ler esta resposta . Observe que UsedRangeé citado como não confiável. Eu acho que isso é enganoso (ou seja, "injusto" UsedRange), pois UsedRangesimplesmente não se destina a relatar a última célula que contém dados. Portanto, não deve ser usado neste caso, conforme indicado nessa resposta. Veja também este comentário .

Para o critério 2, UsedRangeé a opção mais confiável , em comparação com outras opções também projetadas para esse uso. Até torna desnecessário salvar uma pasta de trabalho para garantir que a última célula seja atualizada. Ctrl+ Endirá para uma célula incorreta antes de salvar ("A última célula não será redefinida até você salvar a planilha", em http://msdn.microsoft.com/en-us/library/aa139976%28v=office.10% 29.aspx É uma referência antiga, mas válida a esse respeito).

Para o critério 3, não conheço nenhum método interno . O critério 2 não considera a formatação condicional. Pode-se ter células formatadas, com base em fórmulas, que não são detectadas por UsedRangeou Ctrl+ End. Na figura, a última célula é B3, pois a formatação foi aplicada explicitamente a ela. As células B6: D7 têm um formato derivado de uma regra de Formatação Condicional e isso não é detectado nem por UsedRange. A contabilização disso exigiria alguma programação VBA.

insira a descrição da imagem aqui


Quanto à sua pergunta específica : Qual é a razão por trás disso?

Seu código usa a primeira célula do seu intervalo E4: E48 como um trampolim, para saltar com ele End(xlDown).

A saída "incorreta" será obtida se não houver células não vazias no seu intervalo além da primeira. Então, você está pulando no escuro , ou seja, descendo a planilha (observe a diferença entre as sequências em branco e as vazias !).

Observe que:

  1. Se seu intervalo contiver células não-contíguas e não-vazias, ele também fornecerá um resultado errado.

  2. Se houver apenas uma célula não em branco, mas não for a primeira, seu código ainda fornecerá o resultado correto.

sancho.s ReinstateMonicaCellio
fonte
3
Concordo que é preciso primeiro decidir o que é considerado usado . Eu vejo pelo menos 6 significados. A célula possui: 1) dados, isto é, uma fórmula, possivelmente resultando em um valor em branco; 2) um valor, isto é, uma fórmula ou constante não em branco; 3) formatação; 4) formatação condicional; 5) uma forma (incluindo Comentário) sobrepondo a célula; 6) envolvimento em uma tabela (objeto de lista). Qual combinação você deseja testar? Algumas (como Tabelas) podem ser mais difíceis de testar e outras podem ser raras (como uma forma fora do intervalo de dados), mas outras podem variar de acordo com a situação (por exemplo, fórmulas com valores em branco).
GlennFromIowa 12/04
20

Criei essa função de parada única para determinar a última linha, coluna e célula, seja para dados, células formatadas (agrupadas / comentadas / ocultas) ou formatação 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

Os resultados são assim:
determinar a última célula

Para resultados mais detalhados, algumas linhas no código podem ser descomentadas:
última coluna, linha

Existe uma limitação - se houver tabelas na planilha, os resultados podem se tornar não confiáveis, por isso decidi evitar a execução do código neste 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
ZygD
fonte
2
@ franklin - Acabei de notar uma mensagem na sua caixa de entrada com sua correção, que foi rejeitada pelos revisores. Corrigi esse erro. Eu já usei essa função uma vez quando precisei e vou usá-la novamente, muito obrigado, meu amigo!
ZygD
11

Uma observação importante a ser lembrada ao usar a solução ...

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

... é garantir que sua LastRowvariável seja do Longtipo:

Dim LastRow as Long

Caso contrário, você acabará recebendo erros de SOBRECARGA em determinadas situações nas pastas de trabalho .XLSX

Esta é a minha função encapsulada que uso em vários 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
Bispo
fonte
8

Eu acrescentaria à resposta dada por Siddarth Rout para dizer que a chamada CountA pode ser pulada fazendo com que Find retorne um objeto Range, em vez de um número de linha, e depois teste o objeto Range retornado para ver se é Nothing (planilha em branco) .

Além disso, gostaria que minha versão de qualquer procedimento LastRow retornasse zero para uma planilha em branco, para que eu saiba que está em branco.

não compreende
fonte
8

Gostaria de saber que ninguém mencionou isso, mas a maneira mais fácil de obter a última célula usada é:

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

Isso essencialmente retorna a mesma célula que você recebe Ctrl+ Endapós selecionar Célula A1.

Uma palavra de cautela: o Excel monitora a célula mais à direita que já foi usada em uma planilha. Portanto, se, por exemplo, você digitar alguma coisa em B3 e outra em H8 e depois excluir o conteúdo de H8 , pressionar Ctrl+ Endainda o levará à célula H8 . A função acima terá o mesmo comportamento.

ponto Net
fonte
2
Last Cellno Excel, às vezes se refere a uma célula vazia (de Used Range) diferente de Last Used Cell;).
shA.t
1
O OP precisou apenas da última linha, mas você está certo; a última célula deve ser H5 ; Mas você pode testar sua função após excluir o valor em A5. Você verá que a última célula é a célula vazia, e acho que seu código precisa de algumas edições como essa que Cells(1,1).Select()são inválidas ActiveSheet.Cells(1,1).Select; talvez seja ; Também no VBA não é recomendável usar Select;).
shA.t
5
Isso quebra duas regras fundamentais para o Excel VBA: Não use o Select! E não assuma que a planilha que você deseja é a ativa.
Rachel Hettinger
1
Esta é uma resposta antiga, mas está faltando a Set.
BigBen 11/10/19
8

Como a pergunta original é sobre problemas em encontrar a última célula, nesta resposta listarei as várias maneiras pelas quais você pode obter resultados inesperados ; consulte minha resposta para "Como posso encontrar a última linha que contém dados na planilha do Excel com uma macro?" pela minha opinião sobre como resolver isso.

Começarei expandindo a resposta de sancho.s e o comentário de GlennFromIowa , adicionando ainda mais detalhes:

[...] é preciso primeiro decidir o que é considerado usado. Eu vejo pelo menos 6 significados. A célula possui:

  • 1) dados, isto é, uma fórmula, possivelmente resultando em um valor em branco;
  • 2) um valor, isto é, uma fórmula ou constante não em branco;
  • 3) formatação;
  • 4) formatação condicional;
  • 5) uma forma (incluindo Comentário) sobrepondo a célula;
  • 6) envolvimento em uma tabela (objeto de lista).

Qual combinação você deseja testar? Algumas (como Tabelas) podem ser mais difíceis de testar e outras podem ser raras (como uma forma fora do intervalo de dados), mas outras podem variar de acordo com a situação (por exemplo, fórmulas com valores em branco).

Outras coisas que você pode querer considerar:

  • A) Pode haver linhas ocultas (por exemplo, filtro automático), células em branco ou linhas em branco?
  • B) Que tipo de desempenho é aceitável?
  • C) A macro VBA pode afetar a pasta de trabalho ou as configurações do aplicativo de alguma forma?

Com isso em mente, vamos ver como as maneiras comuns de obter a "última célula" podem produzir resultados inesperados:

  • O .End(xlDown)código da pergunta será quebrado mais facilmente (por exemplo, com uma única célula não vazia ou quando houver células em branco no meio ) pelos motivos explicados na resposta aqui por Siddharth Rout (procure por "xlDown é igualmente não confiável" ).
  • Qualquer solução baseada em Counting ( CountAou Cells*.Count) ou .CurrentRegiontambém será interrompida na presença de células ou linhas em branco.
  • Uma solução que envolve .End(xlUp)pesquisar para trás a partir do final de uma coluna, assim como CTRL + UP, procura dados (as fórmulas que produzem um valor em branco são considerados "dados") nas linhas visíveis (portanto, usá-lo com o filtro automático ativado pode produzir resultados incorretos ⚠️ )

    Você deve tomar cuidado para evitar as armadilhas padrão (para obter detalhes, consultarei novamente a resposta de Siddharth Rout aqui, procure a seção "Encontrar a última linha em uma coluna" ), como codificar a última linha ( Range("A65536").End(xlUp)) em vez de confiar sht.Rows.Count.

  • .SpecialCells(xlLastCell)é equivalente a CTRL + END, retornando a célula mais abaixo e mais à direita do "intervalo usado"; portanto, todas as advertências que se aplicam ao "intervalo usado" também se aplicam a esse método. Além disso, o "intervalo usado" é redefinido apenas ao salvar a pasta de trabalho e ao acessar worksheet.UsedRange, portanto, xlLastCellpode produzir resultados obsoletos⚠️ com modificações não salvas (por exemplo, após a exclusão de algumas linhas). Veja a resposta próxima do dotNET .
  • sht.UsedRange(descrito em detalhes na resposta de sancho.s aqui) considera os dados e a formatação (embora não seja a formatação condicional) e redefine o "intervalo usado" da planilha , que pode ou não ser o que você deseja.

    Observe que um erro comum ️ é usar .UsedRange.Rows.Count⚠️, que retorna o número de linhas no intervalo usado, não o número da última linha (elas serão diferentes se as primeiras linhas estiverem em branco). Para obter detalhes, consulte a resposta do newguy em Como posso encontrar última linha que contém dados na planilha do Excel com uma macro?

  • .Findpermite encontrar a última linha com quaisquer dados (incluindo fórmulas) ou um valor que não esteja em branco em qualquer coluna . Você pode escolher se está interessado em fórmulas ou valores, mas o problema é que redefine os padrões na caixa de diálogo Localizar do Excel ️️⚠️, o que pode ser altamente confuso para os usuários. Também precisa ser usado com cuidado, veja a resposta de Siddharth Rout aqui (seção "Encontrar a última linha em uma planilha" )
  • As soluções mais explícitas que verificam o indivíduo Cellsem um loop geralmente são mais lentas do que reutilizar uma função do Excel (embora ainda possa ter bom desempenho), mas permitem especificar exatamente o que você deseja encontrar. Veja minha solução baseada em UsedRangee matrizes VBA para encontrar a última célula com dados na coluna fornecida - ele lida com linhas ocultas, filtros, espaços em branco, não modifica os padrões de localização e é bastante eficiente.

Qualquer que seja a solução que você escolher, tenha cuidado

  • usar em Longvez de Integerarmazenar os números das linhas (para evitar ficar Overflowcom mais de 65 mil linhas) e
  • para sempre especificar a planilha com a qual você está trabalhando (em Dim ws As Worksheet ... ws.Range(...)vez de Range(...))
  • ao usar .Value(que é a Variant), evite lançamentos implícitos .Value <> ""como eles falharão se a célula contiver um valor de erro.
Nickolay
fonte
4

No entanto, esta pergunta está procurando encontrar a última linha usando o VBA, acho que seria bom incluir uma fórmula de matriz para a função da planilha, pois isso é visitado com freqüência:

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

Você precisa inserir a fórmula sem colchetes e pressionar Shift+ Ctrl+ Enterpara torná-la uma fórmula de matriz.

Isso fornecerá o endereço da última célula usada na coluna D.

M--
fonte
1
Eu gosto disso. Eu posso alterar um pouco para obter apenas o número da linha ... '{= MATCH (ÍNDICE (D: D, MAX (SE (D: D <> "", ROW (D: D) -ROW (D1) +1)) , 1), D: D, 0)} '
PGSystemTester 28/11
3
sub last_filled_cell()
msgbox range("A65536").end(xlup).row
end sub

Aqui, A65536é a última célula na coluna A, este código foi testado no Excel 2003.

Ashwith Ullal
fonte
Você pode explicar como o seu código responde a essa pergunta antiga?
shoover
1
Embora essa resposta seja provavelmente correta e útil, é preferível incluir algumas explicações para explicar como isso ajuda a resolver o problema. Isso se torna especialmente útil no futuro, se houver uma alteração (possivelmente não relacionada) que faça com que ele pare de funcionar e os usuários precisem entender como ela funcionou.
Kevin Brown
2

Eu estava procurando uma maneira de imitar o CTRL+ Shift+ End, para que a solução dotNET seja ótima, exceto no Excel 2010 que eu preciso adicionar um setse quiser evitar um erro:

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

e como verificar isso por si mesmo:

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

Aqui estão meus dois centavos.

IMHO o risco de uma linha oculta com a exclusão de dados é muito significativo para xlUpser considerado uma resposta completa . Concordo que é simples e funcionará a maior parte do tempo, mas apresenta o risco de subestimar a última linha, sem nenhum aviso. Isso poderia produzir CATASTRÓFICOS resultados em algum poinit para alguém que saltou sobre Stack Overlow e estava olhando para "caminho certo" para capturar esse valor.

O Findmétodo é impecável e eu o aprovaria como resposta de uma parada . No entanto, a desvantagem de alterar as Findconfigurações pode ser irritante, principalmente se isso faz parte de uma UDF.

As outras respostas postadas estão bem, no entanto, a complexidade fica um pouco excessiva. Portanto, aqui está minha tentativa de encontrar um equilíbrio entre confiabilidade, complexidade mínima e não uso 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 que isso é bom:

  • Razoavelmente simples, não muitas variáveis.
  • Permite várias colunas.
  • Não modifica Findconfigurações
  • Dinâmico se usado como um UDF com a coluna inteira selecionada.

Por que isso é ruim:

  • Com conjuntos de dados muito grandes e uma lacuna enorme entre o intervalo usado e a última linha nas colunas especificadas, o desempenho será mais lento, em casos raros SIGNIFICANTEMENTE mais lento.

No entanto, acho que uma solução completa que apresenta uma desvantagem de desarrumar as findconfigurações ou executar mais lentamente é uma solução geral melhor. Um usuário pode, então, mexer com suas configurações para tentar melhorar, sabendo o que está acontecendo com seu código. O uso xLUpnão avisa dos riscos potenciais e eles podem continuar por quem sabe há quanto tempo não sabe que seu código não está funcionando corretamente.

PGSystemTester
fonte
1

Nos últimos 3 anos ou mais, estas são as funções que estou usando para encontrar a última linha e a última coluna por coluna definida (para linha) e linha (para coluna):

Última coluna:

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 o caso do OP, esta é a maneira de obter a última linha da coluna E:

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

Última linha, contando linhas vazias com dados:

Aqui, podemos usar as fórmulas conhecidas do Excel , que nos dão a última linha de uma planilha no Excel, sem envolver o VBA -=IFERROR(LOOKUP(2,1/(NOT(ISBLANK(A:A))),ROW(A:A)),0)

Para colocar isso no VBA e não escrever nada no Excel, usando os parâmetros para as últimas funções, algo como isto pode estar em 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
Vityata
fonte
Isso retornará um resultado incorreto se a última linha / coluna estiver oculta.
PGSystemTester 28/11/19
@PGSystemTester - sim, mas, no meu entendimento, quando eu programo, se estiver oculto, não é a última coluna / linha necessária.
Vityata 28/11/19
Ainda bem que funciona para você. Eu suspeito que sua situação não é um caso de uso típico. Mais frequentemente, quando trabalho com clientes que precisam da última linha, eles estão pesquisando a célula mais baixa com dados, e não a célula visível mais baixa com dados. Enfim ... feliz que funciona. PG
PGSystemTester 29/11/19
@PGSystemTester - Entendi seu ponto de vista, mas cuidar da estrutura e não permitir que células invisíveis funcionem como um encanto.
Vityata 29/11/19
@PGSystemTester - sim, se a tarefa permitir linhas vazias, provavelmente eu usaria EVAL()a famosa fórmula do Excel. Embora as pessoas pensem que isso Eval()é mau e essa é outra história interessante sobre a qual escrever ...
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
fonte