Quand je veux trouver la dernière valeur de cellule utilisée, j'utilise:
Dim LastRow As Long
LastRow = Range("E4:E48").End(xlDown).Row
Debug.Print LastRow
Je me trompe de sortie lorsque je mets un seul élément dans une cellule. Mais lorsque je mets plus d'une valeur dans la cellule, la sortie est correcte. Quelle est la raison derrière cela?
NOTE: J'ai l'intention d'en faire un "poste unique" où vous pouvez utiliser la méthode Correct
pour trouver la dernière ligne. Cela couvrira également les meilleures pratiques à suivre pour trouver la dernière ligne. Et par conséquent, je continuerai à le mettre à jour chaque fois que je tomberai sur un nouveau scénario/informations.
Certains des moyens les plus courants de trouver la dernière ligne sont très peu fiables et ne doivent donc jamais être utilisés.
UsedRange
devrait JAMAIS être utilisé pour trouver la dernière cellule contenant des données. C'est très peu fiable. Essayez cette expérience.
Tapez quelque chose dans la cellule A5
. Maintenant, lorsque vous calculez la dernière ligne avec l’une des méthodes indiquées ci-dessous, vous obtenez 5. Maintenant, coloriez la cellule A10
rouge. Si vous utilisez maintenant l'un des codes ci-dessous, vous aurez toujours 5. Si vous utilisez Usedrange.Rows.Count
qu'est-ce que vous obtenez? Ce ne sera pas 5.
Voici un scénario pour montrer comment fonctionne UsedRange
.
xlDown
est également peu fiable.
Considérons ce code
lastrow = Range("A1").End(xlDown).Row
Que se passerait-il s'il n'y avait qu'une seule cellule (A1
) qui contenait des données? Vous finirez par atteindre la dernière ligne de la feuille de calcul! C'est comme si vous sélectionniez la cellule A1
puis appuyez sur End touche puis en appuyant sur Down Arrow clé. Cela vous donnera également des résultats peu fiables s'il y a des cellules vides dans une plage.
CountA
n'est pas fiable non plus car il vous donnera un résultat incorrect s'il y a des cellules vides entre les deux.
Et par conséquent, il faut éviter d'utiliser UsedRange
, xlDown
et CountA
pour trouver la dernière cellule.
Pour trouver la dernière ligne de la colonne E, utilisez cette commande.
With Sheets("Sheet1")
LastRow = .Range("E" & .Rows.Count).End(xlUp).Row
End With
Si vous remarquez que nous avons un .
avant Rows.Count
. Nous avons souvent choisi d'ignorer cela. Voir THIS question sur l’erreur possible que vous pourriez obtenir. Je conseille toujours d'utiliser .
avant Rows.Count
et Columns.Count
. Cette question est un scénario classique dans lequel le code va échouer car le Rows.Count
renvoie 65536
pour Excel 2003 et versions antérieures et 1048576
pour Excel 2007 et versions ultérieures. De même, Columns.Count
renvoie 256
et 16384
, respectivement.
Le fait ci-dessus que Excel 2007+ comporte 1048576
lignes souligne également le fait que nous devons toujours déclarer la variable qui contiendra la valeur de la ligne sous la forme Long
au lieu de Integer
sinon vous obtiendrez un Overflow
error.
Pour trouver la Effective
dernière ligne de la feuille, utilisez ceci. Notez l'utilisation de Application.WorksheetFunction.CountA(.Cells)
. Ceci est nécessaire car s'il n'y a pas de cellules avec des données dans la feuille de calcul, alors .Find
vous donnera 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
Les mêmes principes s'appliquent, par exemple, pour obtenir la dernière ligne de la troisième colonne d'un tableau:
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
Remarque: cette réponse a été motivée par ce commentaire . Le but de UsedRange
diffère de ce qui est mentionné dans la réponse ci-dessus.
Quant à la manière correcte de trouver la dernière cellule utilisée, il faut d’abord décider de ce qui est considéré utilisé , puis sélectionner une méthode appropriée . Je conçois au moins trois significations:
Utilisé = non vide, c'est-à-dire ayant des données .
Used = "... en cours d'utilisation, c'est-à-dire la section qui contient des données ou un formatage ." Selon la documentation officielle , il s'agit du critère utilisé par Excel au moment de la sauvegarde. Voir aussi this . Si ce n’est pas le cas, le critère peut produire des résultats inattendus, mais il peut également être exploité intentionnellement (moins souvent, sûrement), par exemple pour mettre en évidence ou imprimer des régions spécifiques, qui risquent de ne pas contenir de données. Et, bien sûr, il est souhaitable que la plage à utiliser comme critère de sauvegarde lors de la sauvegarde d’un classeur pour éviter de perdre une partie de son travail.
Used = "... en cours d'utilisation, c'est-à-dire la section qui contient des données ou un formatage " ou un formatage conditionnel. Identique à 2., mais en incluant les cellules qui sont la cible de toute règle de mise en forme conditionnelle.
Comment trouver la dernière cellule utilisée dépend de ce que vous voulez (votre critère) .
Pour le critère 1, je suggère de lire cette réponse. Notez que UsedRange
est cité comme non fiable. Je pense que cela est trompeur (c'est-à-dire "injuste" pour UsedRange
), car UsedRange
ne vise tout simplement pas à rapporter la dernière cellule contenant des données. Il ne devrait donc pas être utilisé dans ce cas, comme indiqué dans cette réponse. Voir aussi ce commentaire .
Pour le critère 2, UsedRange
est l'option la plus fiable , par rapport aux autres options également conçues pour cet usage. Il est même inutile de sauvegarder un classeur pour vous assurer que la dernière cellule est mise à jour. Ctrl+End ira dans une mauvaise cellule avant d’enregistrer ("La dernière cellule n’est réinitialisée que lorsque vous enregistrez la feuille de calcul", à partir de http://msdn.Microsoft.com/en-us/library/aa139976%28v=office .10% 29.aspx . C’est une référence ancienne, mais valide à cet égard).
Pour le critère 3, je ne connais aucune méthode intégrée . Le critère 2 ne prend pas en compte la mise en forme conditionnelle. On peut avoir des cellules formatées, basées sur des formules, qui ne sont pas détectées par UsedRange
ou Ctrl+End. Dans la figure, la dernière cellule est B3, car le formatage lui a été explicitement appliqué. Les cellules B6: D7 ont un format dérivé d'une règle de mise en forme conditionnelle, même si UsedRange
ne le détecte pas. La comptabilisation de cela nécessiterait une programmation VBA.
En ce qui concerne votre question spécifique : Quelle est la raison de cela?
Votre code utilise la première cellule de votre plage E4: E48 comme trampoline, pour sauter avec End(xlDown)
.
La sortie "erronée" sera obtenue s'il n'y a pas de cellules non vides dans votre plage autre que peut-être la première. Ensuite, vous sautez dans le noir , c’est-à-dire en bas de la feuille de travail (vous devriez noter la différence entre vierge et chaîne vide !).
Notez que:
Si votre plage contient des cellules non vides non contiguës, cela donnera également un résultat erroné.
S'il n'y a qu'une seule cellule non vide, mais que ce n'est pas la première, votre code vous donnera toujours le résultat correct.
J'ai créé cette fonction unique pour déterminer la dernière ligne, colonne et cellule, que ce soit pour des données, des cellules formatées (groupées/commentées/masquées) ou une mise en forme conditionnelle.
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
Les résultats ressemblent à ceci:
Pour des résultats plus détaillés, certaines lignes du code peuvent être décommentées:
Une limitation existe: s'il y a des tables dans la feuille, les résultats peuvent devenir peu fiables. J'ai donc décidé d'éviter d'exécuter le code dans ce cas:
If .ListObjects.Count > 0 Then
MsgBox "Cannot return reliable results, as there is at least one table in the worksheet."
Exit Sub
End If
Une note importante à garder à l'esprit lors de l'utilisation de la solution ...
LastRow = ws.Cells.Find(What:="*", After:=ws.range("a1"), SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
... est de s'assurer que votre variable LastRow
est de Long
type:
Dim LastRow as Long
Sinon, vous obtiendrez des erreurs OVERFLOW dans certaines situations dans les classeurs .XLSX
C’est ma fonction encapsulée que j’aborde dans diverses utilisations de code.
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
J'ajouterais à la réponse donnée par Siddarth Rout de dire que l'appel CountA peut être ignoré en faisant en sorte que Find renvoie un objet Range au lieu d'un numéro de ligne, puis teste l'objet Range renvoyé pour voir s'il est Nothing (feuille de calcul vierge). .
De plus, je demanderais à ma version de toute procédure LastRow de renvoyer un zéro pour une feuille de calcul vierge, pour que je sache qu'elle est vide.
Je me demande si personne n’a mentionné cela, mais le moyen le plus simple d’obtenir la dernière cellule utilisée est:
_Function GetLastCell(sh as Worksheet) As Range
GetLastCell = sh.Cells(1,1).SpecialCells(xlLastCell)
End Function
_
Cela renvoie essentiellement la même cellule que vous obtenez par Ctrl + End après avoir sélectionné Cell A1
.
Un mot d'avertissement: Excel garde la trace de la cellule la plus en bas à droite jamais utilisée dans une feuille de calcul. Donc, si, par exemple, vous entrez quelque chose dans B et quelque chose d'autre dans H8, puis supprimez plus tard le contenu de H8, en appuyant sur Ctrl + End vous conduira toujours à H8 cellule. La fonction ci-dessus aura le même comportement.
Puisque la question initiale concerne des problèmes pour trouver la dernière cellule, dans cette réponse, je vais énumérer les différentes manières d'obtenir des résultats inattendus; voir ma réponse à "Comment trouver la dernière ligne contenant des données dans la feuille Excel avec une macro?" pour résoudre ce problème.
Je vais commencer par développer la réponse de sancho.s et le commentaire de GlennFromIowa , en ajoutant encore plus de détails:
[...] il faut d'abord décider de ce qui est considéré comme utilisé. Je vois au moins 6 significations. La cellule a:
- 1) des données, c’est-à-dire une formule, pouvant éventuellement donner une valeur vide;
- 2) une valeur, c'est-à-dire une formule ou une constante non vide;
- 3) mise en forme;
- 4) mise en forme conditionnelle;
- 5) une forme (y compris un commentaire) recouvrant la cellule;
- 6) implication dans une table (objet liste).
Quelle combinaison voulez-vous tester? Certains (tels que les tableaux) peuvent être plus difficiles à tester, et certains peuvent être rares (comme une forme en dehors de la plage de données), mais d'autres peuvent varier en fonction de la situation (par exemple, les formules avec des valeurs vides).
Autres points à considérer:
Dans cet esprit, voyons comment les méthodes courantes pour obtenir la "dernière cellule" peuvent produire des résultats inattendus:
.End(xlDown)
de la question se cassera le plus facilement (par exemple avec ne seule cellule non vide ou lorsqu'il y a cellules vides entre) pour les raisons expliquées in la réponse de Siddharth Rout ici (recherchez "" xlDown est également peu fiable. ") ????Count
ing (CountA
ou Cells*.Count
) ou .CurrentRegion
sera également interrompue en présence de cellules ou de lignes vides ????.End(xlUp)
pour rechercher en arrière à partir de la fin d'une colonne, tout comme CTRL + UP, recherchera data (les formules produisant une valeur vide sont considérées comme des "données") dans - lignes visibles (son utilisation avec le filtre automatique activé risque alors de produire des résultats incorrects). Vous devez veiller à éviter les pièges classiques (pour plus de détails, nous reviendrons à la réponse de Siddharth Rout ici, recherchez le "Trouver la dernière ligne dans une colonne. " section), telle que coder en dur la dernière ligne (Range("A65536").End(xlUp)
) au lieu de s'appuyer sur sht.Rows.Count
.
.SpecialCells(xlLastCell)
est équivalent à CTRL + FIN, renvoyant la cellule la plus basse et la plus à droite de la "plage utilisée". Par conséquent, tous les avertissements qui s'appliquent à l'utilisation de la "plage utilisée" s'appliquent également à cette méthode. De plus, la "plage utilisée" n'est réinitialisée que lors de la sauvegarde du classeur et de l'accès à worksheet.UsedRange
, de sorte que xlLastCell
puisse produire des résultats obsolètes avec des modifications non enregistrées (par exemple, après la suppression de certaines lignes). Voir le réponse à proximité par dotNET .sht.UsedRange
(décrit en détail dans la réponse de sancho.s ici) considère les données et le formatage (bien que le formatage ne soit pas conditionnel) et réinitialise le "gamme utilisée" de la feuille de calcul, qui peut ne pas être ce que vous voulez. Notez qu'une erreur courante ️ est d'utiliser .UsedRange.Rows.Count
⚠️, qui renvoie le nombre de lignes dans la plage utilisée, et non le dernier numéro de ligne (ils seront différents si les premières lignes sont vides), voir réponse de newguy à . Comment trouver la dernière ligne contenant des données dans la feuille Excel avec une macro?
.Find
vous permet de trouver la dernière ligne avec toutes les données (y compris les formules) ou une valeur non vide dans n'importe quelle colonne. Vous pouvez choisir si vous êtes intéressé par des formules ou des valeurs, mais le problème est que cela réinitialise les valeurs par défaut dans la boîte de dialogue Rechercher d'Excel ️️⚠️, ce qui peut être très déroutant pour vos utilisateurs. Il doit également être utilisé avec précaution, voir réponse de Siddharth Rout ici (section "Trouver la dernière ligne d'une feuille" )Cells
'dans une boucle sont généralement plus lentes que de réutiliser une fonction Excel (même si elles peuvent toujours être performantes), mais vous permettent de spécifier exactement ce que vous voulez trouver. Voir ma solution sur la base de UsedRange
et des tableaux VBA pour trouver la dernière cellule avec les données de la colonne donnée - elle gère les lignes cachées, les filtres, les blancs, ne modifie pas les valeurs par défaut de Find et est assez performant.Quelle que soit la solution que vous choisissez, soyez prudent
Long
au lieu de Integer
pour stocker les numéros de ligne (pour éviter d'obtenir Overflow
avec plus de 65 000 lignes) etDim ws As Worksheet ... ws.Range(...)
au lieu de Range(...)
).Value
(qui est un Variant
), évitez les conversions implicites telles que .Value <> ""
, car elles échoueront si la cellule contient une valeur d'erreur.sub last_filled_cell()
msgbox range("a65536").end(xlup).row
end sub
"ici a65536 est la dernière cellule de la colonne où ce code a été testé sur Excel sti72003" 200
et si vous utilisez son "a1,048,576"
mon code est juste pour que les débutants comprennent les concepts de ce que fin (xlup) et d'autres commandes associées peuvent faire
Cependant, si cette question cherche à trouver la dernière ligne à l'aide de VBA, je pense qu'il serait bon d'inclure une formule matricielle pour la fonction de feuille de calcul, car celle-ci est visitée fréquemment:
{=ADDRESS(MATCH(INDEX(D:D,MAX(IF(D:D<>"",ROW(D:D)-ROW(D1)+1)),1),D:D,0),COLUMN(D:D))}
Vous devez entrer la formule sans crochets, puis appuyer sur Shift + Ctrl + Enter pour en faire une formule matricielle.
Cela vous donnera l'adresse de la dernière cellule utilisée dans la colonne D.
Je cherchais un moyen de mimer le CTRL+Shift+EndDonc, la solution dotNET est excellente, sauf avec Excel 2010, je dois ajouter un set
si je veux éviter une erreur:
Function GetLastCell(sh As Worksheet) As Range
Set GetLastCell = sh.Cells(1, 1).SpecialCells(xlLastCell)
End Function
et comment vérifier cela par vous-même:
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
Pour les 3 dernières années, ce sont les fonctions que j'utilise pour trouver la dernière ligne et la dernière colonne par colonne définie (pour ligne) et ligne (pour colonne):
Function lastCol(Optional wsName As String, Optional rowToCheck As Long = 1) As Long
Dim ws As Worksheet
If wsName = vbNullString Then
Set ws = ActiveSheet
Else
Set ws = Worksheets(wsName)
End If
lastCol = ws.Cells(rowToCheck, ws.Columns.Count).End(xlToLeft).Column
End Function
Function lastRow(Optional wsName As String, Optional columnToCheck As Long = 1) As Long
Dim ws As Worksheet
If wsName = vbNullString Then
Set ws = ActiveSheet
Else
Set ws = Worksheets(wsName)
End If
lastRow = ws.Cells(ws.Rows.Count, columnToCheck).End(xlUp).Row
End Function
Pour le cas de l'OP, voici comment obtenir la dernière ligne de la colonne E
:
Debug.Print lastRow(columnToCheck:=Range("E4:E48").Column)
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