1. まず結論(何ができるの?)
Excel VBA の Range.End プロパティを使うと、あるセルから見て値が入っている「最後のセル」(下・上・右・左いずれか)を簡単に取得できます。
実務でよく使うのは「データの最終行」「最終列」を自動で見つけること —— データの量が変わってもマクロを壊さずに処理できます。
2. Range.End の基本イメージ(図を頭に描いて)
Range("A1").End(xlDown)
→ A1 から下に向かって「連続しているセル」をたどり、空白に出会う直前(もしくはブロックの最後)のセルを返す。- 方向は
xlDown, xlUp, xlToRight, xlToLeftの 4種類。
重要な直感:End は Excel のキーボード操作の Ctrl + ↓(あるいは Ctrl + →)と同じ動作をします。
3. なぜ注意が必要か(典型的な落とし穴)
Range.End は「途中に空白セルがあるとそこで止まる」ので、表の途中に空白行や空白列があると期待した最終行を返さないことがあります。
例:A列にデータがあって途中で空白があると、Range("A1").End(xlDown) は空白の直前で止まる → 本当の最終行がもっと下にあっても見つけられない。
4. 実務でよく使う安全で安定したパターン
a) 列に対する最終行(もっともポピュラー)
Dim lastRow As Long
lastRow = Cells(Rows.Count, "A").End(xlUp).Row
VB解説:
Rows.Countはワークシートの最終行番号(Excel 2010以降は 1048576)を返す。- その一番下(
Cells(Rows.Count, "A"))からxlUp(上方向)へ探すと、途中の空白に影響されずにその列で最後に値が入っている行が取得できます。
b) 行に対する最終列
Dim lastCol As Long
lastCol = Cells(1, Columns.Count).End(xlToLeft).Column
VB解説:行1の右端から左に探すパターン。列数を右端(Columns.Count)から探すので、途中の空白列に影響されにくい。
c) シート全体の最終セル(より堅牢に)
Dim lastCell As Range
Set lastCell = ActiveSheet.Cells.Find(What:="*", _
After:=ActiveSheet.Cells(1,1), _
LookIn:=xlFormulas, _
LookAt:=xlPart, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious)
If Not lastCell Is Nothing Then
Debug.Print "最終使用セルは: " & lastCell.Address
End If
VB解説:
Cells.Findを使うと、シート全体で最後に使われているセル(値や数式が入っているセル)を確実に見つけられます。SearchDirection:=xlPreviousとSearchOrder:=xlByRowsがポイント。
5. 実際に動く「よくある例題」を3つ(コピペして使える)
例題A:A列の最終行を取得して、その範囲をメッセージ表示
Sub ExampleA()
Dim lastRow As Long
lastRow = Cells(Rows.Count, "A").End(xlUp).Row
MsgBox "A列の最終行は " & lastRow & " 行目です。"
End Sub
VB例題B:ヘッダ(1行目)を除くデータを別シートにコピー
Sub CopyDataExcludingHeader()
Dim wsSrc As Worksheet, wsDst As Worksheet
Dim lastRow As Long, lastCol As Long
Set wsSrc = ThisWorkbook.Worksheets("Sheet1")
Set wsDst = ThisWorkbook.Worksheets("Sheet2")
lastRow = wsSrc.Cells(wsSrc.Rows.Count, "A").End(xlUp).Row
lastCol = wsSrc.Cells(1, wsSrc.Columns.Count).End(xlToLeft).Column
If lastRow < 2 Then
MsgBox "データがありません(ヘッダのみ)"
Exit Sub
End If
wsSrc.Range(wsSrc.Cells(2, 1), wsSrc.Cells(lastRow, lastCol)).Copy _
Destination:=wsDst.Cells(1, 1)
MsgBox "コピー完了: " & lastRow - 1 & " 行をコピーしました。"
End Sub
VBポイント:ヘッダ行(1行目)を除いたデータだけをコピーしたい場合、データの開始行を 2 にする。
例題C:最後の行までループして合計を計算(A列の合計)
Sub SumColumnA()
Dim lastRow As Long
Dim i As Long
Dim total As Double
lastRow = Cells(Rows.Count, "A").End(xlUp).Row
total = 0
For i = 1 To lastRow
If IsNumeric(Cells(i, "A").Value) Then
total = total + Cells(i, "A").Value
End If
Next i
MsgBox "A列の合計 = " & total
End Sub
VBワンポイント:値のタイプをチェックする IsNumeric を入れると安全。
6. よくある応用パターン(テンプレ)
- 動的範囲を
Range(Cells(1,1), Cells(lastRow, lastCol))で作り、その範囲を一括で処理(コピー・集計・書式変更など)。 - 大量データを扱うなら、セルごとの処理を避けて 配列に読み込んで処理 → 結果を一括で書き戻す(速度が格段に上がる)。
7. さらに堅牢にする小さな工夫
- 「列に全くデータがない(空列)」のケースを考える:
If WorksheetFunction.CountA(Columns("A")) = 0 Then
MsgBox "A列は空です"
Else
lastRow = Cells(Rows.Count, "A").End(xlUp).Row
End If
VBUsedRangeも使えるが、ユーザー操作でゴミ(表示上は空でも使用履歴が残るセル)があると最終セルが大きく誤差を出すことがある。Find("*", ...)の方が実務では信頼性が高い。
8. 練習問題
問題1(初級)
Sheet1 の B列にデータがある。最終行を取得して、最終行の下に「END」と書き込むマクロを書きなさい。
解答(模範)
Sub Practice1()
Dim lr As Long
lr = Cells(Rows.Count, "B").End(xlUp).Row
Cells(lr + 1, "B").Value = "END"
End Sub
VB問題2(中級)
Sheet1 のデータ(1行目はヘッダ)を Sheet2 に同じ列幅でコピーするマクロを書きなさい。データの最終行・最終列は動的に検出すること。
解答(模範)
Sub Practice2()
Dim ws1 As Worksheet, ws2 As Worksheet
Dim lr As Long, lc As Long
Set ws1 = Worksheets("Sheet1")
Set ws2 = Worksheets("Sheet2")
lr = ws1.Cells(ws1.Rows.Count, 1).End(xlUp).Row
lc = ws1.Cells(1, ws1.Columns.Count).End(xlToLeft).Column
ws1.Range(ws1.Cells(1, 1), ws1.Cells(lr, lc)).Copy Destination:=ws2.Cells(1, 1)
End Sub
VBまとめ(初心者が覚えるべき3つ)
Range.Endは便利だが 途中空白に弱い → 列の最終行は下端からxlUpで探すのが安定。- シート全体の最後を確実に知りたいときは
Cells.Find(What:="*", ... SearchDirection:=xlPrevious)を使う。 - 大量行を扱うなら、1セルずつ処理するより配列で一括処理する方が速い(これは次のステップで学ぶと良い)。
