Excel VBAのループ – For Each, For Next, Do While など
In this Article
VBAで効率的に作業するためには、ループの理解は必須です。
ループは、コードブロックを設定された回数だけ繰り返したり、オブジェクトの集合内の各オブジェクトに対してコードブロックを繰り返すことを可能にします。 まず、ループがどのような機能を持つのか、いくつかの例を挙げて説明します。その後、ループのすべてをお教えします。
VBAループの簡単な例
For Eachループ
For Eachループは、ワークブック内のすべてのワークシートや範囲内のすべてのセルなど、コレクション内のすべてのオブジェクトをループ処理します。
ワークブック内のすべてのワークシートをループする
このコードは、ワークブック内のすべてのワークシートをループし、もし非表示に設定されていたとしても、全てのシートを見えるようにします。
Sub LoopThroughSheets()
Dim ws As Worksheet
For Each ws In Worksheets
ws.Visible = True
Next
End Sub
範囲内のすべてのセルをループする
このコードは、セルの範囲内をループし、セルの値が負、正、またはゼロであるかどうかをテストします。
Sub If_Loop()
Dim Cell as Range
For Each Cell In Range("A2:A6")
If Cell.Value > 0 Then
Cell.Offset(0, 1).Value = "正"
ElseIf Cell.Value < 0 Then
Cell.Offset(0, 1).Value = "負"
Else
Cell.Offset(0, 1).Value = "ゼロ"
End If
Next Cell
End Sub
For Nextループ
Forループのもう一つのタイプは、For Nextループです。 For Nextループは、整数の範囲をループすることができます。 このコードでは、1から10までの整数をループして、それぞれをメッセージボックスで表示します。
Sub ForLoop()
Dim i As Integer
For i = 1 To 10
MsgBox i
Next i
End Sub
Do While ループ
Do Whileループは、ある条件を満たしている間(While)、ループします。以下のコードでは1から10までの整数をループし、それぞれをメッセージボックスで表示します。
Sub DoWhileLoop()
Dim n As Integer
n = 1
Do While n < 11
MsgBox n
n = n + 1
Loop
End Sub
Do Untilループ
逆に、Do Until Loopsは、ある条件が満たされるまで(Until)ループします。このコードでは、前の2つの例と同じことをします。
Sub DoUntilLoop()
Dim n As Integer
n = 1
Do Until n > 10
MsgBox n
n = n + 1
Loop
End Sub
後述しますが、Do WhileやDo Untilのループを作る際には、無限ループとならないように細心の注意を払う必要があります。
VBA Loop Builder
上の画像はAutoMacroに含まれるプレミアムVBAアドインの「Loop Builder」のスクリーンショットです。Loop Builderを使うと、異なるオブジェクトや数値をループするループをすばやく簡単に構築できます。各オブジェクトに対してアクションを実行したり、特定の条件を満たすオブジェクトだけを選択したりすることができます。
このアドインには、他にも多くのコードビルダー、豊富なVBAコードライブラリ、コーディングツールの数々が含まれており、VBAの開発者にとって必携のアドインです。
それでは、さまざまな種類のループを徹底的に解説していきます。
VBA For Next ループ
Forループの構文
For Nextループを使用すると、コードのブロックを指定した回数だけ繰り返すことができます。構文は次のとおりです。
[Dim カウンター変数 as Long]
For カウンター変数 = 開始値 to 終了値 [ステップ値]
[任意の処理]
Next [カウンター変数]
ここで、括弧内の項目はオプションです。
- [Dim カウンター変数 as Long] – カウンター変数を宣言します。Option Explicit がモジュールの先頭で宣言されている場合は、必須です。
- カウンター変数 – カウントに使われる整数型変数です。
- 開始値 – カウントの開始値です。 (例: 1)
- 終了値 – カウントの終了値です。 (例: 10)
- [ステップ値] – 整数1個ごとではなく、整数n個ごとにカウントできるようにします。また、負の値で逆に数えることもできます (例: Step -1)
- [任意の処理] – 実際に繰り返すコード。
- Next [カウンター変数] – For Nextループの終了位置を知らせます。カウンター変数は入れても入れなくても構いません。しかし、コードを読みやすくするために、カウンター変数を含めることを強くお勧めします。
わかりにくいかもしれませんが、ご安心ください。いくつかの例題を確認します。
10まで数える
このコードでは、For-Nextループを使って10まで数えます。
Sub ForEach_CountTo10()
Dim n As Integer
For n = 1 To 10
MsgBox n
Next n
End Sub
For ループステップ
10まで数える – 偶数のみ
このコードでは、偶数だけを数えて10まで数えます。
Sub ForEach_CountTo10_Even()
Dim n As Integer
For n = 2 To 10 Step 2
MsgBox n
Next n
End Sub
「Step 2」を追加していることに注目してください。これは、Forループのカウンター変数を2ずつ「ステップ」するように指示しています。 また、負のステップ値を使用することで、逆にステップすることもできます。
For Loop Step – Inverse
10からのカウントダウン
このコードは10からのカウントダウンを行います。
Sub ForEach_Countdown_Inverse()
Dim n As Integer
For n = 10 To 1 Step -1
MsgBox n
Next n
MsgBox "Lift Off"
End Sub
セルが空白の場合、行を削除する
私が最もよく使うのは、負のステップ値のForループを用いてセル範囲をループし、ある条件を満たした行を削除する方法です。 上の行から下の行へループさせると、行を削除するときにカウンタが狂ってしまうためです。
この例では、最下行から開始して空白のセルがある行を削除します。
Sub ForEach_DeleteRows_BlankCells()
Dim n As Integer
For n = 10 To 1 Step -1
If Range("a" & n).Value = "" Then
Range("a" & n).EntireRow.Delete
End If
Next n
End Sub
ネストされたForループ
Forループの中に別のForループを「ネスト(入れ子)」することができます。ここでは、ネストされたForループを使って、乗算表を作成します。
Sub Nested_ForEach_MultiplicationTable()
Dim row As Integer, col As Integer
For row = 1 To 9
For col = 1 To 9
Cells(row + 1, col + 1).Value = row * col
Next col
Next row
End Sub
Exit For
Exit Forステートメントを使用すると、 For Nextループを直ちに終了させることができます。 通常、Exit ForはIfステートメントと一緒に使用し、特定の条件が満たされた場合にFor Nextループを終了させます。
例えば、あるセルを探すためにForループを使用するとします。そのセルが見つかったら、ループを終了してコードを高速化することができます。
このコードでは、1行目から1000行目までをループして、A列の「error」を探します。もし見つかったら、セルを選択した状態にして、見つかったエラーを警告してループを終了します。
Sub ExitFor_Loop()
Dim i As Integer
For i = 1 To 1000
If Range("A" & i).Value = "error" Then
Range("A" & i).Select
MsgBox "エラーが見つかりました"
Exit For
End If
Next i
End Sub
重要:ネストされた For Loop の場合、Exit For は現在の For Loop を終了するだけで、アクティブな Loop をすべて終了するわけではありません。
Continue For
VBAには、Visual Basicにある「Continue」コマンドはありません。代わりに “Exit “を使用する必要があります。
VBAのFor Eachループ
VBAのFor Eachループは、コレクション内のすべてのオブジェクトをループします。
- 範囲内のすべてのセル
- ワークブック内の全ワークシート
- ワークシート内の全図形
- 開いているすべてのワークブック
また、ネストされたFor Eachループを使用することもできます。
- すべてのワークシートの範囲内のすべてのセル
- 全ワークシート上の全図形
- 開いているすべてのワークブックのすべてのシート
- などなど…
構文は以下のとおりです。
For Each オブジェクト in コレクション
[任意の処理]
Next [オブジェクト]
ここで
- オブジェクト – レンジ、ワークシート、ワークブック、図形などを表す変数。(例:rng)
- コレクション – オブジェクトのコレクションです。(例:Range(“a1:a10”)
- [任意の処理] – 各オブジェクトで実行されるコードブロックです。
- Next [オブジェクト] – クロージングステートメント。[Object] はオプションですが、強くお勧めします。
範囲内の各セルをループ
このコードでは、範囲内の各セルをループします。
Sub ForEachCell_inRange()
Dim cell As Range
For Each cell In Range("a1:a10")
cell.Value = cell.Offset(0,1).Value
Next Cell
End Sub
ワークブック内の各ワークシートをループ
このコードは、ワークブック内のすべてのワークシートをループし、各シートのプロテクトを解除するものです。
Sub ForEachSheet_inWorkbook()
Dim ws As Worksheet
For Each ws In Worksheets
ws.Unprotect "password"
Next ws
End Sub
開かれている各ワークブックをループ
このコードでは、開いているすべてのワークブックを保存して閉じます。
Sub ForEachWB_inWorkbooks()
Dim wb As Workbook
For Each wb In Workbooks
wb.Close SaveChanges:=True
Next wb
End Sub
ワークシート内の各図形をループ
このコードは、アクティブシート内のすべての図形を削除します。
Sub ForEachShape()
Dim shp As Shape
For Each shp In ActiveSheet.Shapes
shp.Delete
Next shp
End Sub
ワークブック内の各ワークシートの各図形をループ
For Eachループを入れ子にすることもできます。ここでは、アクティブワークブック内のすべてのワークシートにあるすべての図形をループします。
Sub ForEachShape_inAllWorksheets()
Dim shp As Shape, ws As Worksheet
For Each ws In Worksheets
For Each shp In ws.Shapes
shp.Delete
Next shp
Next ws
End Sub
For Each – IF ループ
前にも述べたように、ループの中でIf文を使って、ある条件を満たしたときだけ動作を実行することができます。 このコードは、ある範囲内のセルが空白だった場合、そのセルが含まれる行全体を非表示にします。
Sub ForEachCell_inRange()
Dim cell As Range
For Each cell In Range("a1:a10")
If cell.Value = "" Then _
cell.EntireRow.Hidden = True
Next cell
End Sub
VBA Do While ループ
VBAのDo WhileとDo Until(次章参照)は非常によく似ています。これらは、ある条件を満たしている間(または、満たされるまで)、ループを繰り返します。
Do Whileループは、ある条件を満たしている間、ループを繰り返します。
以下はDo Whileの構文です。
Do While 条件
[任意の処理]
Loop
ここで
- 条件 – テストする条件
- [任意の処理] – 繰り返すコードブロック
ループの最後にConditionを設定したDo Whileループを設定することも可能です。
Do
[任意の処理]
Loop While 条件
それぞれをデモして、どのように違うかを説明します。
Do While
冒頭でも紹介したDo Whileループの例です。
Sub DoWhileLoop()
Dim n As Integer
n = 1
Do While n < 11
MsgBox n
n = n + 1
Loop
End Sub
Loop While
今度は条件をループの最後に移動してみましょう。実行する内容は変わりません。
Sub DoLoopWhile()
Dim n As Integer
n = 1
Do
MsgBox n
n = n + 1
Loop while n < 11
End Sub
VBA Do Untilループ
Do Until Loopは、ある条件を満たすまでループを繰り返します。構文は基本的にDo Whileループと同じです。
Do Until 条件
[任意の処理]
Loop
Do Whileループと同様、条件の位置はループの最初でも最後でもかまいません。
Do
[任意の処理]
Loop Until 条件
Do Until
このDo Untilループは、前の例と同じように10まで数えます。
Sub DoUntilLoop()
Dim n As Integer
n = 1
Do Until n > 10
MsgBox n
n = n + 1
Loop
End Sub
Loop Until
このLoop Untilループも、10までカウントします。
Sub DoLoopUntil()
Dim n As Integer
n = 1
Do
MsgBox n
n = n + 1
Loop Until n > 10
End Sub
Doループの終了
Exit ForでForループを抜けるのと同じように、Exit DoコマンドでDoループをすぐに抜けることができます。
Exit Do
以下は、Exit Doの例です。
Sub ExitDo_Loop()
Dim i As Integer
i = 1
Do Until i > 1000
If Range("A" & i).Value = "error" Then
Range("A" & i).Select
MsgBox "エラーが見つかりました"
Exit Do
End If
i = i + 1
Loop
End Sub
ループの終了または中断
前述したように、ループを抜けるにはExit ForやExit Doを使用します。
Exit For
Exit Do
ただし、これらのコマンドは、ループを実行する前にコードに追加しておく必要があります。
現在実行中のループを「壊す」場合は、キーボードでESCや CTRL+Pause Breakを押してみるとよいでしょう。ただし、これはうまくいかないかもしれません。 うまくいかない場合は、ループが終了するのを待つか、無限ループの場合は、CTRL+ALT+ DeleteでExcelを強制終了する必要があります。
このため、私はDoループを避けるようにしています。誤って無限ループを作成すると、Excelを再起動しなければならず、作業内容が失われる可能性があるからです。
その他のループの例
行をループする
これは、ある列のすべての行をループするものです。
Public Sub LoopThroughRows()
Dim cell As Range
For Each cell In Range("A:A")
If cell.value <> "" Then MsgBox cell.address & ": " & cell.Value
Next cell
End Sub
列をループする
これは、1つの行のすべての列をループします。
Public Sub LoopThroughColumns()
Dim cell As Range
For Each cell In Range("1:1")
If cell.Value <> "" Then MsgBox cell.Address & ": " & cell.Value
Next cell
End Sub
フォルダー内のファイルをループする
このコードは、フォルダー内のすべてのファイルをループして、リストを作成します。
Sub LoopThroughFiles ()
Dim oFSO As Object
Dim oFolder As Object
Dim oFile As Object
Dim i As Integer
Set oFSO = CreateObject("Scripting.FileSystemObject")
Set oFolder = oFSO.GetFolder("C:\Demo")
i = 2
For Each oFile In oFolder.Files
Range("A" & i).value = oFile.Name
i = i + 1
Next oFile
End Sub
配列のループ処理
このコードは、配列 ‘arrList’ の中をループします。
For i = LBound(arrList) To UBound(arrList)
MsgBox arrList(i)
Next i
LBound関数は配列の「下限」を、UBoundは「上限」を取得します。
Access VBAのループ
上記の例のほとんどは、Access VBAでも動作します。ただし、AccessではRangeオブジェクトではなく、Recordsetオブジェクトでループします。
Sub LoopThroughRecords()
On Error Resume Next
Dim dbs As Database
Dim rst As Recordset
Set dbs = CurrentDb
Set rst = dbs.OpenRecordset("tblClients", dbOpenDynaset)
With rst
.MoveLast
.MoveFirst
Do Until .EOF = True
MsgBox (rst.Fields("ClientName"))
.MoveNext
Loop
End With
rst.Close
Set rst = Nothing
Set dbs = Nothing
End Sub