回答:
注:私は、これを「ワンストップポスト」にCorrect
して、最後の行を見つける方法を使用できるようにするつもりです。これは、最後の行を見つけるときに従うべきベストプラクティスもカバーします。したがって、新しいシナリオや情報に遭遇した場合は、常に更新を続けます。
非常に信頼性が低く、したがって使用してはならない最後の行を見つける最も一般的な方法のいくつか。
UsedRange
必要があります決してデータを持っている最後のセルを検索するために使用しないこと。それは非常に信頼できません。この実験を試してください。
セルに何かを入力しますA5
。ここで、以下のいずれかの方法で最後の行を計算すると、5が得られます。今度はセルをA10
赤に着色します。以下のコードのいずれかを使用しても、5が引き続き取得されます。Usedrange.Rows.Count
何を使用すると、何が得られますか?5にはなりません。
これがどのようにUsedRange
機能するかを示すシナリオです。
xlDown
同様に信頼できません。
このコードを検討してください
lastrow = Range("A1").End(xlDown).Row
A1
データを持つセル()が1つだけの場合はどうなりますか?ワークシートの最後の行に到達することになります!セルA1
を選択してEndキーを押してからキーを押すようDown Arrowなものです。また、範囲内に空白のセルがある場合、信頼性の低い結果が得られます。
CountA
また、間に空白のセルがあると誤った結果が得られるため、信頼性が低くなります。
そしてそれゆえ一つはの使用は避けるべきであるUsedRange
、xlDown
とCountA
最後のセルを見つけること。
列Eの最後の行を見つけるには、これを使用します
With Sheets("Sheet1")
LastRow = .Range("E" & .Rows.Count).End(xlUp).Row
End With
.
以前に気づいた場合Rows.Count
。私たちはしばしばそれを無視することを選びました。発生する可能性のあるエラーについては、この質問を参照してください。私はいつも使用して助言する.
前に、Rows.Count
とColumns.Count
。この質問は、Excel 2003以前およびExcel 2007以降で結果Rows.Count
が返さ65536
れるためにコードが失敗するという典型的なシナリオ1048576
です。同様Columns.Count
に256
と16384
をそれぞれ返します。
エクセル2007+が持っている以上のこと1048576
も、行は、私たちはいつものように行の値を保持する変数を宣言すべきであるという事実に強調Long
の代わりに、Integer
他のあなたが得るOverflow
エラーを。
このアプローチは非表示の行をスキップすることに注意してください。上記のA列のスクリーンショットを振り返ると、行8が非表示の場合、このアプローチはの5
代わりに戻ります8
。
Effective
シートの最後の行を見つけるには、これを使用します。の使用に注意してくださいApplication.WorksheetFunction.CountA(.Cells)
。ワークシートのデータとは、細胞が存在しないならば、これは必要とされ.Find
、あなたを与えるだろうRun Time Error 91: Object Variable or With block variable not set
With Sheets("Sheet1")
If Application.WorksheetFunction.CountA(.Cells) <> 0 Then
lastrow = .Cells.Find(What:="*", _
After:=.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
Else
lastrow = 1
End If
End With
同じ原則が適用されます。たとえば、テーブルの3番目の列の最後の行を取得します。
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
Usedrange.Rows.Count
何を使用すると、何が得られますか?5にはなりません。usedrangeは、最後の行を見つけるのに非常に信頼性が低くなります。
remembers
最後の設定です。手動でを実行した場合でもFind
、最後の設定が記憶されます。これは、この「事実」を知っていれば実際には朗報です
UsedRange
(データのある最後のセルを見つけるのは非常に信頼できない)というあなたの説明は誤解を招くと思います。UsedRange
場合によっては正しい結果が得られることもありますが、単にその目的を意図したものではありません。提案された実験は混乱を増すと思います。UsedRange
($ A $ 1:$ A $ 8)で得られる結果は、最初にデータを入力して削除することに依存しません。右側の図は、データを入力して削除しなくても同じです。私の答えを見てください。
注:この回答は、このコメントによって動機付けられました。の目的はUsedRange
、上記の回答で述べられているものとは異なります。
最後に使用されたセルを見つける正しい方法については、最初に使用されていると見なされるものを決定し、次に適切な方法を選択する必要があります。少なくとも3つの意味があると思います。
使用済み=空白ではない、つまりデータがある。
使用済み= "...使用中、つまりデータまたはフォーマットを含むセクションを意味します。" 公式文書によると、これは保存時にExcelで使用される基準です。この公式ドキュメントも参照してください。これに気付いていない場合、この基準は予期しない結果をもたらす可能性がありますが、意図的に悪用されることもあります(頻度は低く、確実ではありません)。そしてもちろん、ワークブックの保存時に使用する範囲の基準として、自分の作業の一部を失わないようにすることが望ましいです。
使用済み= "...使用中、つまりデータまたはフォーマットを含むセクション" または条件付きフォーマット。 2.と同じですが、条件付き書式ルールのターゲットであるセルも含まれます。
最後に使用セルが何に依存してどのように見つけるかあなたは(あなたの基準を)したいです。
基準1については、この回答を読むことをお勧めします。UsedRange
は信頼できないものとして引用されていることに注意してください。データを含む最後のセルを報告することを単に意図していないのでUsedRange
、それは誤解を招く(つまり、「への不公平」)だと思いUsedRange
ます。したがって、その回答に示されているように、この場合は使用しないでください。このコメントも参照してください。
基準2では、UsedRange
これもこの用途向けに設計された他のオプションと比較して、最も信頼できるオプションです。最後のセルが確実に更新されるように、ブックを保存する必要もありません。
Ctrl+ End保存する前に間違ったセルに移動します(「最後のセルはワークシートを保存するまでリセットされません」、http://msdn.microsoft.com/en-us/library/aa139976%28v=office.10%から
29.aspx。これは古いリファレンスですが、この点では有効です)。
基準3については、組み込みの方法を知りません。基準2は条件付き書式を考慮していません。UsedRange
またはCtrl+で検出されない数式に基づいてセルをフォーマットした可能性がありますEnd。図では、書式設定が明示的に適用されているため、最後のセルはB3です。セルB6:D7には条件付き書式ルールから派生した書式があり、これもによって検出されませんUsedRange
。これを説明するには、いくつかのVBAプログラミングが必要です。
あなたの特定の質問について: これの背後にある理由は何ですか?
コードは、範囲E4:E48の最初のセルをトランポリンとして使用し、でジャンプしEnd(xlDown)
ます。
「誤った」出力は、おそらく最初のもの以外の範囲内に空白でないセルがない場合に取得されます。次に、暗闇の中で、つまりワークシートを下に跳んでいます(空白と空の文字列の違いに注意してください!)。
ご了承ください:
範囲に隣接していない空白でないセルが含まれている場合も、誤った結果になります。
空白でないセルが1つしかなく、それが最初のセルではない場合でも、コードは正しい結果を提供します。
データ、フォーマット済み(グループ化/コメント/非表示)セル、条件付きフォーマットなど、最後の行、列、セルを決定するためのこのワンストップ関数を作成しました。
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
結果は次のようになります。
より詳細な結果を得るには、コードの一部の行のコメントを解除できます。
制限が1つあります。シートにテーブルがあると、結果が信頼できなくなる可能性があるため、この場合はコードを実行しないようにします。
If .ListObjects.Count > 0 Then
MsgBox "Cannot return reliable results, as there is at least one table in the worksheet."
Exit Sub
End If
ソリューションを使用する際の注意点...
LastRow = ws.Cells.Find(What:="*", After:=ws.range("a1"), SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
... LastRow
変数のLong
タイプを確認することです:
Dim LastRow as Long
そうしないと、.XLSXワークブックの特定の状況でOVERFLOWエラーが発生します。
これは、カプセル化された関数で、さまざまなコードを使用します。
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
誰もこれについて言及していませんが、最後に使用されたセルを取得する最も簡単な方法は次のとおりです。
Function GetLastCell(sh as Worksheet) As Range
GetLastCell = sh.Cells(1,1).SpecialCells(xlLastCell)
End Function
これは基本的に、Cellを選択した後にCtrl+で取得した同じセルを返します。EndA1
注意:Excelは、ワークシートで使用された最も右下のセルを追跡します。したがって、たとえば、B3に何かを入力し、H8に何かを入力して、後でH8の内容を削除した場合でも、Ctrl+ Endを押すとH8セルに移動します。上記の関数は同じ動作をします。
Last Cell
ExcelではUsed Range
、Last Used Cell
;)とは異なる空のセル(から)を参照することがあります。
Cells(1,1).Select()
、それは多分ある無効ですActiveSheet.Cells(1,1).Select
。また、VBAではSelect
;)の使用は推奨されません。
Set
。
元の質問は最後のセルの検索に関する問題なので、この回答では、予期しない結果を得ることができるさまざまな方法をリストします。「マクロを使用してExcelシートのデータを含む最後の行を見つけるにはどうすればよいですか?」に対する私の回答を参照してください。これを解決するための私の見解のために。
私は、上に拡大することから始めましょうsancho.sによって解答とGlennFromIowaによってコメントにも詳細を追加し、:
[...]最初に何が使用されていると見なされるかを決定する必要があります。私は少なくとも6つの意味を見ます。セルは:
- 1)データ、つまり数式。結果として空白になる可能性があります。
- 2)値、つまり空白でない式または定数。
- 3)フォーマット;
- 4)条件付きフォーマット。
- 5)セルと重なる形状(コメントを含む)。
- 6)テーブル(リストオブジェクト)への関与。
どの組み合わせをテストしますか?いくつか(テーブルなど)はテストがより難しく、いくつかはまれ(データ範囲外の形状など)かもしれませんが、その他は状況(例:値が空白の数式)によって異なる場合があります。
あなたが考慮したいと思うかもしれない他のもの:
それを念頭に置いて、「最後のセル」を取得する一般的な方法が予期しない結果をどのように生成できるかを見てみましょう。
.End(xlDown)
質問のコードは、ここのSiddharth Routの回答で説明されている理由により(たとえば、1つの空でないセルがある場合、または間に空白のセルがある場合)、簡単に破損します(「xlDownも同様に信頼できない」を検索します)。👎Count
ing(CountA
またはCells*.Count
)に基づくソリューション、または.CurrentRegion
空白のセルまたは行があると破損する👎.End(xlUp)
列の末尾から逆方向に検索するソリューションでは、CTRL + UPと同様に、表示されている行でデータ(空白の値を生成する式は「データ」と見なされます)を探します(オートフィルターを有効にして使用すると、誤った結果が生成される可能性があります⚠️ )。最後の行をハードコーディングするなど、標準の落とし穴を回避するよう注意する必要があります(詳細については、ここでSiddharth Routの回答を再度参照します。「列の最後の行を検索」セクションを探してくださいRange("A65536").End(xlUp)
)。に依存する代わりにsht.Rows.Count
。
.SpecialCells(xlLastCell)
CTRL + ENDと同じで、「使用範囲」の一番下のセルと右端のセルを返します。したがって、「使用範囲」に依存することに適用されるすべての警告は、このメソッドにも適用されます。さらに、「使用範囲」は、ワークブックを保存するときとにアクセスするときにのみリセットされるためworksheet.UsedRange
、xlLastCell
変更が保存されていない(たとえば、一部の行が削除された後など)で古い結果が生じる可能性があります。dotNETによる近くの回答を参照してください。sht.UsedRange
(ここのsancho.sによる回答で詳細に説明されています)は、データとフォーマットの両方(条件付きフォーマットではありません)を考慮し、ワークシートの「使用範囲」をリセットします。注よくある間違いは、使用に️isことを.UsedRange.Rows.Count
返す⚠️、行数を使用範囲ではなく、最後の行番号は、詳細に関しては、(最初の数行が空白の場合、彼らは異なります)にnewguyの答えを私は見つけることができますどのようにマクロを含むExcelシートのデータを含む最後の行?
.Find
任意のデータ(数式を含む)または任意の列の空白以外の値を含む最後の行を検索できます。数式と値のどちらに関心があるかを選択できますが、問題は、Excelの[検索]ダイアログ ️️⚠️ のデフォルトをリセットすることです。これは、ユーザーを混乱させる可能性があります。また、慎重に使用する必要があります。ここのSiddharth Routの回答を参照してください(「シートの最後の行を検索する」セクション)Cells
ループで 'を個別にチェックするより明示的なソリューションは、一般にExcel関数を再利用するよりも低速です(ただし、パフォーマンスは向上します)が、検索対象を正確に指定できます。指定された列のデータを含む最後のセルを見つけるには、およびVBA配列に基づく私のソリューションを参照してくださいUsedRange
-非表示の行、フィルター、空白を処理し、Findのデフォルトを変更せず、非常に高いパフォーマンスを発揮します。どんな解決策を選ぶにせよ、注意してください
Long
代わりに使用するInteger
(Overflow
65,000行を超えることを避けるため)およびDim ws As Worksheet ... ws.Range(...)
代わりにRange(...)
).Value
(あるVariant
)のような暗黙のキャスト避ける.Value <> ""
セルがエラー値が含まれている場合、彼らは失敗しますように。ただし、この質問はVBAを使用して最後の行を見つけることを目的としています。ワークシート関数の配列数式を含めると、頻繁にアクセスされるため、これを含めるとよいでしょう。
{=ADDRESS(MATCH(INDEX(D:D,MAX(IF(D:D<>"",ROW(D:D)-ROW(D1)+1)),1),D:D,0),COLUMN(D:D))}
大括弧なしで数式を入力してからShift+ Ctrl+ Enterを押して、数式を配列数式にする必要があります。
これにより、列Dで最後に使用されたセルのアドレスが表示されます。
sub last_filled_cell()
msgbox range("A65536").end(xlup).row
end sub
ここでは、A65536このコードは、Excel 2003の上でテストされた列Aの最後のセルです。
CTRL+ Shift+ を模倣する方法を探していたEndので、dotNETソリューションは素晴らしいですが、Excel 2010でset
エラーを回避したい場合は、追加する必要があります。
Function GetLastCell(sh As Worksheet) As Range
Set GetLastCell = sh.Cells(1, 1).SpecialCells(xlLastCell)
End Function
これを自分で確認する方法:
Sub test()
Dim ws As Worksheet, r As Range
Set ws = ActiveWorkbook.Sheets("Sheet1")
Set r = GetLastCell(ws)
MsgBox r.Column & "-" & r.Row
End Sub
これが私の2セントです。
私見では、データが除外された非表示の行のリスクは非常に大きいためxlUp
、ワンストップの回答と見なすことはできません。私はそれが単純でほとんどの場合うまくいくことに同意しますが、警告なしに最後の行を過小評価するリスクを提示します。これは、スタックオーバーローにジャンプし、この値を取得するための「確実な方法」を模索していた人にとって、いくつかの点でCATASTROPHICの結果を生成する可能性があります。
このFind
方法は完璧であり、私はそれをワンストップ回答として承認します。ただしFind
、特にこれがUDFの一部である場合は、設定を変更することの欠点が煩わしい場合があります。
投稿された他の回答は問題ありませんが、複雑さが少し過剰になります。したがって、ここでは、信頼性、最小限の複雑さ、およびを使用しないことのバランスを見つけるための私の試みを示しますFind
。
Function LastRowNumber(Optional rng As Range) As Long
If rng Is Nothing Then
Set rng = ActiveSheet.UsedRange
Else
Set rng = Intersect(rng.Parent.UsedRange, rng.EntireColumn)
If rng Is Nothing Then
LastRowNumber = 1
Exit Function
ElseIf isE = 0 Then
LastRowNumber = 1
Exit Function
End If
End If
LastRowNumber = rng.Cells(rng.Rows.Count, 1).Row
Do While IsEmpty(Intersect(rng, _
rng.Parent.Rows(LastRowNumber)))
LastRowNumber = LastRowNumber - 1
Loop
End Function
なぜこれが良いのか:
Find
設定を変更しませんなぜこれが悪いのか:
ただし、find
設定をめちゃくちゃにするか、パフォーマンスが低下するという欠点のあるワンストップソリューションの方が、全体的なソリューションとしては優れていると思います。次に、ユーザーは設定をいじくり回して、コードで何が行われているのかを理解しながら、改善を試みることができます。を使用xLUp
しても、潜在的なリスクが警告されることはなく、コードが正しく機能していないことを知らないことを誰が知っているかについて、彼らは引き継ぐことができます。
過去3年以上の間、これらは、定義された列(行の)および行(列の)ごとに最後の行と最後の列を見つけるために使用している関数です。
Function lastCol(Optional wsName As String, Optional rowToCheck As Long = 1) As Long
Dim ws As Worksheet
If wsName = vbNullString Then
Set ws = ActiveSheet
Else
Set ws = Worksheets(wsName)
End If
lastCol = ws.Cells(rowToCheck, ws.Columns.Count).End(xlToLeft).Column
End Function
Function lastRow(Optional wsName As String, Optional columnToCheck As Long = 1) As Long
Dim ws As Worksheet
If wsName = vbNullString Then
Set ws = ActiveSheet
Else
Set ws = Worksheets(wsName)
End If
lastRow = ws.Cells(ws.Rows.Count, columnToCheck).End(xlUp).Row
End Function
OPの場合、これはcolumnの最後の行を取得する方法E
です。
Debug.Print lastRow(columnToCheck:=Range("E4:E48").Column)
ここでは、VBAを使用せずに、Excelのワークシートの最後の行を取得する、よく知られたExcelの数式を使用できます-=IFERROR(LOOKUP(2,1/(NOT(ISBLANK(A:A))),ROW(A:A)),0)
これをVBAに配置し、Excelで何も記述しないようにするには、後者の関数のパラメーターを使用して、次のようなことが考えられます。
Public Function LastRowWithHidden(Optional wsName As String, Optional columnToCheck As Long = 1) As Long
Dim ws As Worksheet
If wsName = vbNullString Then
Set ws = ActiveSheet
Else
Set ws = Worksheets(wsName)
End If
Dim letters As String
letters = ColLettersGenerator(columnToCheck)
LastRowWithHidden = ws.Evaluate("=IFERROR(LOOKUP(2,1/(NOT(ISBLANK(" & letters & "))),ROW(" & letters & " )),0)")
End Function
Function ColLettersGenerator(col As Long) As String
Dim result As Variant
result = Split(Cells(1, col).Address(True, False), "$")
ColLettersGenerator = result(0) & ":" & result(0)
End Function
EVAL()
有名なExcel式を使用します。人々はそれEval()
を悪だと思うかもしれませんが、これは書くべきもう一つの興味深い話です...
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