ループ処理 (For / Do)

にメンテナンス済み

VBA でプログラムを書く際、「同じ処理を何度も繰り返す」場面は非常に多く発生します。セルの値を1行ずつ処理したり、シートを順番に操作したり、条件を満たすまでデータを探したり——こうした繰り返し処理を実現するのが**ループ(反復処理)**です。

VBA には主に For…NextFor Each…NextDo While…LoopDo Until…Loop の4種類のループ構文があります。この記事では、それぞれの構文と使い方、使い分けの基準、そして実務で役立つテクニックまで詳しく解説します。

ループ処理が必要となるシチュエーション

実務では、以下のような場面でループ処理が必要になります:

  • データの一括処理: 表の全行に対して同じ計算や書式設定を行う
  • シートの順次操作: すべてのシートに同じ処理を適用する
  • 条件付き検索: 特定の条件を満たすデータが見つかるまで探す
  • データの集計: 売上データを1件ずつ加算して合計を算出する
  • ファイルの一括処理: フォルダ内のすべてのファイルに対して処理を行う

For…Next(回数指定ループ)

基本構文

For...Next は、繰り返す回数が事前にわかっている場合に使用するループです。カウンター変数を使って、開始値から終了値まで1ずつ増やしながら処理を繰り返します。

for_next_syntax.bas
' 構文
For カウンター変数 = 開始値 To 終了値
    ' 繰り返す処理
Next カウンター変数

基本的な使用例

for_next_basic.bas
Sub ForNextBasic()
    Dim i As Long

    ' 1 から 5 まで繰り返す
    For i = 1 To 5
        Debug.Print "現在の値: " & i
    Next i
    ' => 現在の値: 1
    ' => 現在の値: 2
    ' => 現在の値: 3
    ' => 現在の値: 4
    ' => 現在の値: 5
End Sub
チェック

Next の後のカウンター変数名は省略可能です(Next だけでも動作します)。ただし、ネストしたループでは可読性のために記述することをお勧めします。

セルへの連番入力

for_next_cells.bas
Sub FillSerialNumbers()
    Dim i As Long

    ' A1 から A10 まで連番を入力
    For i = 1 To 10
        Cells(i, 1).Value = i
    Next i
End Sub

Step キーワード(増分の指定)

Step を使うと、カウンター変数の増分を変更できます。

for_next_step.bas
Sub ForNextStep()
    Dim i As Long

    ' 2 ずつ増やす(偶数のみ)
    Debug.Print "--- 偶数 ---"
    For i = 2 To 10 Step 2
        Debug.Print i
    Next i
    ' => 2, 4, 6, 8, 10

    ' 3 ずつ増やす
    Debug.Print "--- 3の倍数 ---"
    For i = 3 To 15 Step 3
        Debug.Print i
    Next i
    ' => 3, 6, 9, 12, 15
End Sub

逆順ループ(Step に負の値)

Step に負の値を指定すると、大きい値から小さい値に向かってループできます。

for_next_reverse.bas
Sub ForNextReverse()
    Dim i As Long

    ' 10 から 1 まで逆順
    For i = 10 To 1 Step -1
        Debug.Print i
    Next i
    ' => 10, 9, 8, 7, 6, 5, 4, 3, 2, 1
End Sub
逆順ループでは Step -1 を忘れないこと

For i = 10 To 1 と書くだけではループは1回も実行されません。既定の Step は 1 なので、10 から 1 に向かって増やすことはできず、即座にループが終了します。逆順にするには必ず Step -1(または負の値)を指定してください。

行の削除は逆順ループで

行を削除する処理では、**下から上に向かって処理する(逆順ループ)**のが鉄則です。上から順に削除すると、行がずれてしまい正しく処理できません。

' ○ 逆順ループで行を削除
For i = lastRow To 1 Step -1
    If Cells(i, 1).Value = "" Then
        Rows(i).Delete
    End If
Next i

For Each…Next(コレクションの反復)

基本構文

For Each...Next は、コレクション(集合)内のすべての要素を1つずつ処理するためのループです。シートの一覧、セル範囲、配列などの要素を順番に取り出します。

for_each_syntax.bas
' 構文
For Each 要素変数 In コレクション
    ' 各要素に対する処理
Next 要素変数

ワークシートの一括処理

for_each_sheets.bas
Sub ForEachSheets()
    Dim ws As Worksheet

    ' すべてのシートの名前を出力
    For Each ws In ThisWorkbook.Worksheets
        Debug.Print ws.Name
    Next ws
End Sub

セル範囲の処理

for_each_range.bas
Sub ForEachRange()
    Dim cell As Range

    ' A1:A10 の各セルを処理
    For Each cell In Range("A1:A10")
        If cell.Value <> "" Then
            Debug.Print cell.Address & ": " & cell.Value
        End If
    Next cell
End Sub

配列の処理

for_each_array.bas
Sub ForEachArray()
    Dim fruits As Variant
    fruits = Array("りんご", "みかん", "ばなな", "ぶどう")

    Dim item As Variant
    For Each item In fruits
        Debug.Print item
    Next item
    ' => りんご
    ' => みかん
    ' => ばなな
    ' => ぶどう
End Sub
For Each の要素変数は Variant またはオブジェクト型で宣言する

配列を For Each で処理する場合、要素変数は Variantで宣言する必要があります。StringLong 型で宣言するとエラーになります。

' ○ 正しい
Dim item As Variant
For Each item In Array("A", "B", "C")

' × エラーになる
Dim item As String
For Each item In Array("A", "B", "C")

ただし、WorksheetRange などのオブジェクトコレクションの場合は、対応するオブジェクト型で宣言できます。

For Next と For Each の比較

特徴For…NextFor Each…Next
繰り返しの基準カウンター変数(数値)コレクションの要素
インデックス操作i で自由にアクセス可能△ インデックスは直接使えない
逆順処理Step -1 で可能× 不可
コレクション処理△ やや冗長○ 簡潔に書ける
可読性数値処理向きコレクション処理向き

Do While…Loop(条件が True の間ループ)

基本構文

Do While...Loop は、条件が True である間、処理を繰り返します。繰り返す回数が事前にわからない場合に使用します。

do_while_syntax.bas
' 構文(前判定): 条件が False なら1回も実行されない
Do While 条件式
    ' 繰り返す処理
Loop

' 構文(後判定): 最低1回は実行される
Do
    ' 繰り返す処理
Loop While 条件式

基本的な使用例

do_while_basic.bas
Sub DoWhileBasic()
    Dim count As Long
    count = 1

    Do While count <= 5
        Debug.Print "カウント: " & count
        count = count + 1
    Loop
    ' => カウント: 1
    ' => カウント: 2
    ' => カウント: 3
    ' => カウント: 4
    ' => カウント: 5
End Sub

セルの値を順次処理(空白セルまで)

do_while_cells.bas
Sub DoWhileCells()
    Dim i As Long
    i = 1

    ' A列のセルを空白になるまで処理
    Do While Cells(i, 1).Value <> ""
        Debug.Print "行 " & i & ": " & Cells(i, 1).Value
        i = i + 1
    Loop
End Sub

後判定ループ(Do…Loop While)

条件を後ろで判定する形式では、ループ内の処理が最低1回は実行されます。

do_loop_while.bas
Sub DoLoopWhile()
    Dim answer As String

    Do
        answer = InputBox("「はい」と入力すると終了します")
    Loop While answer <> "はい"

    MsgBox "終了しました"
End Sub
チェック

前判定(Do While...Loop)と後判定(Do...Loop While)の違いは、条件チェックのタイミングです。ユーザー入力を受け付ける場面など、「まず1回は処理を実行したい」場合には後判定を使います。

Do Until…Loop(条件が True になるまでループ)

基本構文

Do Until...Loop は、条件が True になるまで(つまり条件が False の間)処理を繰り返します。Do While の条件を反転させたものです。

do_until_syntax.bas
' 構文(前判定)
Do Until 条件式
    ' 繰り返す処理
Loop

' 構文(後判定)
Do
    ' 繰り返す処理
Loop Until 条件式

基本的な使用例

do_until_basic.bas
Sub DoUntilBasic()
    Dim count As Long
    count = 1

    ' count が 6 になるまで繰り返す
    Do Until count > 5
        Debug.Print "カウント: " & count
        count = count + 1
    Loop
    ' => カウント: 1
    ' => カウント: 2
    ' => カウント: 3
    ' => カウント: 4
    ' => カウント: 5
End Sub

Do While と Do Until の比較

同じ処理を Do WhileDo Until で書き比べると、条件式が反転する関係にあることがわかります。

while_vs_until.bas
' Do While: 条件が True の間ループ
Do While Cells(i, 1).Value <> ""
    i = i + 1
Loop

' Do Until: 条件が True になるまでループ(同じ動作)
Do Until Cells(i, 1).Value = ""
    i = i + 1
Loop
チェック

Do WhileDo Until は機能的に同じことができます。条件式がより自然に読める方を選びましょう。一般的には Do While(〜の間は繰り返す)の方が直感的に理解しやすいとされています。

無限ループに注意

Do While / Do Until では、ループ内で条件が変化する処理を必ず入れる必要があります。条件が永遠に満たされない(または永遠に満たされたまま)だと、処理が止まらなくなります(無限ループ)。

' × 危険:無限ループ(count が変化しない)
Dim count As Long
count = 1
Do While count <= 5
    Debug.Print count
    ' count = count + 1 を忘れている!
Loop

万が一無限ループに入ってしまった場合は、Ctrl + Break(または Esc)キーで強制停止できます。

Exit 文によるループの途中脱出

Exit For

For...NextFor Each...Next のループを途中で抜けるには Exit For を使用します。

exit_for.bas
Sub ExitForExample()
    Dim i As Long

    ' 特定の値を見つけたらループを抜ける
    For i = 1 To 100
        If Cells(i, 1).Value = "完了" Then
            Debug.Print "「完了」が見つかりました: 行 " & i
            Exit For
        End If
    Next i

    ' ループ終了後の処理
    If i > 100 Then
        Debug.Print "「完了」は見つかりませんでした"
    End If
End Sub

Exit Do

Do While...LoopDo Until...Loop のループを途中で抜けるには Exit Do を使用します。

exit_do.bas
Sub ExitDoExample()
    Dim attempt As Long
    Dim password As String
    attempt = 0

    Do While True  ' 無限ループ
        attempt = attempt + 1
        password = InputBox("パスワードを入力してください(試行 " & attempt & "/3)")

        If password = "secret" Then
            MsgBox "ログイン成功!"
            Exit Do
        End If

        If attempt >= 3 Then
            MsgBox "試行回数の上限に達しました"
            Exit Do
        End If
    Loop
End Sub
Forループのスキップ(Continue)
VBAのForループ内で次のループへスキップする方法を紹介します。GoToコマンドを使うことで、Forループ内で次のループへスキップすることができます。

ループのネスト(入れ子)

ループの中にさらにループを書くことをネスト(入れ子)と呼びます。二次元的なデータ(行と列)を処理する場合に頻繁に使用します。

二重ループの基本

nested_loop_basic.bas
Sub NestedLoopBasic()
    Dim row As Long
    Dim col As Long

    ' 5行 × 3列 のセルに値を入力
    For row = 1 To 5
        For col = 1 To 3
            Cells(row, col).Value = "R" & row & "C" & col
        Next col
    Next row
    ' => A1="R1C1", B1="R1C2", C1="R1C3"
    ' => A2="R2C1", B2="R2C2", C2="R2C3"
    ' => ...
End Sub

九九の表を作成

nested_loop_multiplication.bas
Sub MultiplicationTable()
    Dim i As Long
    Dim j As Long

    For i = 1 To 9
        For j = 1 To 9
            Cells(i, j).Value = i * j
        Next j
    Next i
End Sub

ネストしたループの Exit For

Exit For はネストしたループの場合、最も内側のループのみを抜けます。外側のループも抜けたい場合は、フラグ変数を使うか、GoTo 文を使用します。

nested_exit_for.bas
Sub NestedExitFor()
    Dim row As Long
    Dim col As Long
    Dim found As Boolean
    found = False

    For row = 1 To 100
        For col = 1 To 10
            If Cells(row, col).Value = "エラー" Then
                Debug.Print "エラーが見つかりました: " & Cells(row, col).Address
                found = True
                Exit For  ' 内側のループのみ抜ける
            End If
        Next col

        If found Then Exit For  ' 外側のループも抜ける
    Next row
End Sub
ネストは浅く保つ

ループのネストが3段階以上になると、コードの可読性が大幅に低下します。深いネストが必要な場合は、内側のループを別のプロシージャに分割することを検討しましょう。

4種類のループの使い分け

比較表

特徴For…NextFor Each…NextDo While…LoopDo Until…Loop
繰り返し条件回数指定コレクションの要素数条件が True の間条件が True になるまで
回数がわかる場合◎ 最適
コレクション処理◎ 最適
回数が不明な場合◎ 最適◎ 最適
逆順処理×
インデックスアクセス×
無限ループのリスクなしなしありあり

使い分けの基準

For…Next が適しているケース:

  • 繰り返す回数が事前にわかっている
  • カウンター変数(行番号など)を使う必要がある
  • 逆順で処理する必要がある(行の削除など)

For Each…Next が適しているケース:

  • ワークシートやセル範囲などのコレクションを処理する
  • 配列の全要素を順番に処理する
  • インデックスが不要な場合

Do While / Do Until が適しているケース:

  • 繰り返す回数が事前にわからない
  • 特定の条件を満たすまで(または満たす間)処理を続ける
  • ユーザー入力を繰り返し受け付ける
loop_choice.bas
' ○ For...Next: 回数が決まっている
Sub Example_ForNext()
    Dim i As Long
    For i = 1 To 10
        Cells(i, 1).Value = i * 100
    Next i
End Sub

' ○ For Each: コレクションの全要素を処理
Sub Example_ForEach()
    Dim ws As Worksheet
    For Each ws In ThisWorkbook.Worksheets
        ws.Tab.Color = RGB(200, 200, 255)
    Next ws
End Sub

' ○ Do While: 終了条件が動的
Sub Example_DoWhile()
    Dim i As Long
    i = 1
    Do While Cells(i, 1).Value <> ""
        Cells(i, 2).Value = UCase(Cells(i, 1).Value)
        i = i + 1
    Loop
End Sub

実践的な活用例

例1:条件に一致するデータの集計

practical_sum_by_condition.bas
Option Explicit

Sub SumByDepartment()
    Dim ws As Worksheet
    Set ws = ThisWorkbook.Sheets("売上データ")

    Dim lastRow As Long
    lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row

    Dim totalSales As Currency
    Dim count As Long
    totalSales = 0
    count = 0

    ' A列が「営業部」のデータの B列(売上)を合計
    Dim i As Long
    For i = 2 To lastRow  ' 2行目から(ヘッダー除く)
        If ws.Cells(i, 1).Value = "営業部" Then
            totalSales = totalSales + ws.Cells(i, 2).Value
            count = count + 1
        End If
    Next i

    Debug.Print "営業部の売上合計: " & Format(totalSales, "#,##0") & " 円"
    Debug.Print "対象件数: " & count & " 件"

    Set ws = Nothing
End Sub

例2:すべてのシートに対して処理を実行

practical_all_sheets.bas
Option Explicit

Sub FormatAllSheets()
    Dim ws As Worksheet

    For Each ws In ThisWorkbook.Worksheets
        ' 各シートの1行目をヘッダーとして書式設定
        With ws.Rows(1)
            .Font.Bold = True
            .Font.Size = 12
            .Interior.Color = RGB(68, 114, 196)
            .Font.Color = RGB(255, 255, 255)
        End With

        Debug.Print ws.Name & " のヘッダーを書式設定しました"
    Next ws
End Sub

例3:重複データの検出

practical_find_duplicates.bas
Option Explicit

Sub FindDuplicates()
    Dim ws As Worksheet
    Set ws = ThisWorkbook.Sheets("データ")

    Dim lastRow As Long
    lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row

    Dim i As Long
    Dim j As Long
    Dim duplicateCount As Long
    duplicateCount = 0

    ' 二重ループで重複を検出
    For i = 2 To lastRow - 1
        For j = i + 1 To lastRow
            If ws.Cells(i, 1).Value = ws.Cells(j, 1).Value Then
                ws.Cells(j, 1).Interior.Color = RGB(255, 200, 200)
                duplicateCount = duplicateCount + 1
            End If
        Next j
    Next i

    If duplicateCount > 0 Then
        MsgBox duplicateCount & " 件の重複が見つかりました", vbExclamation
    Else
        MsgBox "重複はありません", vbInformation
    End If

    Set ws = Nothing
End Sub

例4:入力値の検証ループ

practical_input_validation.bas
Option Explicit

Sub InputWithValidation()
    Dim inputValue As Variant
    Dim isValid As Boolean
    isValid = False

    Do Until isValid
        inputValue = InputBox("1〜100 の数値を入力してください")

        ' キャンセルされた場合
        If StrPtr(inputValue) = 0 Then
            MsgBox "キャンセルされました"
            Exit Sub
        End If

        ' 数値チェック
        If Not IsNumeric(inputValue) Then
            MsgBox "数値を入力してください", vbExclamation
        ElseIf CLng(inputValue) < 1 Or CLng(inputValue) > 100 Then
            MsgBox "1〜100 の範囲で入力してください", vbExclamation
        Else
            isValid = True
        End If
    Loop

    MsgBox "入力された値: " & inputValue, vbInformation
End Sub

パフォーマンスの最適化

ループ処理は大量のデータを扱う場合にパフォーマンスに大きく影響します。以下のテクニックを活用して、処理速度を改善しましょう。

画面更新と自動計算の一時停止

performance_optimization.bas
Sub OptimizedLoop()
    ' パフォーマンス最適化
    Application.ScreenUpdating = False
    Application.Calculation = xlCalculationManual
    Application.EnableEvents = False

    Dim i As Long
    For i = 1 To 10000
        Cells(i, 1).Value = i * 2
    Next i

    ' 設定を戻す
    Application.EnableEvents = True
    Application.Calculation = xlCalculationAutomatic
    Application.ScreenUpdating = True
End Sub

配列を使った高速処理

セルを1つずつ読み書きするよりも、配列にまとめて読み込み、処理後に一括で書き戻す方が圧倒的に高速です。

performance_array.bas
Sub FastLoopWithArray()
    Dim ws As Worksheet
    Set ws = ThisWorkbook.Sheets("データ")

    Dim lastRow As Long
    lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row

    ' セルの値を配列に一括読み込み
    Dim data As Variant
    data = ws.Range("A1:B" & lastRow).Value

    ' 配列上で処理(セルアクセスなし = 高速)
    Dim i As Long
    For i = 1 To UBound(data, 1)
        If data(i, 1) <> "" Then
            data(i, 2) = data(i, 1) * 1.1  ' 10%増し
        End If
    Next i

    ' 配列の値をセルに一括書き戻し
    ws.Range("A1:B" & lastRow).Value = data

    Set ws = Nothing
End Sub
大量データの処理は配列を活用する

1万行以上のデータを処理する場合、セルを1つずつ操作するループと配列を使うループでは、処理速度に数十倍〜数百倍の差が出ることがあります。大量データを扱うループでは積極的に配列を活用しましょう。

実行速度の改善
出来上がったものを実行してみると、処理中に画面がガタガタ動いたり、マウスが頻繁に処理中モードと通常モードに切り替わってしまうことはありませんか?そういった方向けに、作成した処理の前に読み込ませるだけで、簡単に上記の問題が解決され、さらに高速

注意点とよくある間違い

1. カウンター変数の型は Long を使う

counter_type.bas
' × Integer は 32,767 までしか扱えない
Dim i As Integer  ' 大量データでオーバーフローの危険

' ○ Long を使えば約21億まで扱える
Dim i As Long     ' 安全
チェック

VBA では行番号を扱うカウンター変数には常に Long 型を使いましょう。Excel のシートは最大 1,048,576 行あるため、Integer 型(最大 32,767)ではオーバーフローする可能性があります。

2. For Each でコレクションを変更しない

For Each のループ中にコレクションの要素を追加・削除すると、予期しない動作やエラーが発生します。

for_each_modify.bas
' × 危険:ループ中にシートを削除
Sub DangerousLoop()
    Dim ws As Worksheet
    For Each ws In ThisWorkbook.Worksheets
        If ws.Name Like "temp*" Then
            ws.Delete  ' ループ中の削除は危険!
        End If
    Next ws
End Sub

' ○ 安全:逆順の For...Next ループで削除
Sub SafeDeleteLoop()
    Dim i As Long
    Application.DisplayAlerts = False
    For i = ThisWorkbook.Worksheets.Count To 1 Step -1
        If ThisWorkbook.Worksheets(i).Name Like "temp*" Then
            ThisWorkbook.Worksheets(i).Delete
        End If
    Next i
    Application.DisplayAlerts = True
End Sub

3. Do ループのカウンター更新忘れ

do_loop_forget_increment.bas
' × 無限ループになる
Sub InfiniteLoop()
    Dim i As Long
    i = 1
    Do While i <= 10
        Debug.Print i
        ' i = i + 1 を忘れている!
    Loop
End Sub

' ○ 正しい
Sub CorrectLoop()
    Dim i As Long
    i = 1
    Do While i <= 10
        Debug.Print i
        i = i + 1  ' カウンターを必ず更新
    Loop
End Sub

練習問題

次のコードの実行後、変数 total の値はいくつですか?
Dim total As Long
Dim i As Long
total = 0

For i = 1 To 5
    total = total + i
Next i
正解は「15」です。ループでは i が 1 から 5 まで順に変化し、total に加算されます。1 + 2 + 3 + 4 + 5 = 15 です。
次のコードの実行結果として正しいものはどれですか?
Dim i As Long
For i = 10 To 1
    Debug.Print i
Next i
正解は「何も出力されない」です。For i = 10 To 1 では Step が既定値の 1 なので、開始値 10 が終了値 1 より大きく、ループは1回も実行されません。逆順にするには For i = 10 To 1 Step -1 と書く必要があります。
すべてのワークシートに対して処理を行いたい場合、最も適切なループはどれですか?
正解は「For Each ws In ThisWorkbook.Worksheets」です。ワークシートはコレクション(集合)であり、For Each を使うと全要素を簡潔かつ安全に処理できます。シート数をハードコーディングする For i = 1 To 10 は実際のシート数と合わない可能性があり、Do ループは冗長です。

まとめ

  • For…Next は繰り返す回数が事前にわかっている場合に使用する。Step で増分を変更でき、Step -1 で逆順ループが可能
  • For Each…Next はコレクション(シート一覧、セル範囲、配列)の全要素を処理する場合に最適。要素変数は Variant またはオブジェクト型で宣言する
  • Do While…Loop は条件が True の間ループを継続する。繰り返し回数が不明な場合に使用する
  • Do Until…Loop は条件が True になるまでループを継続する。Do While と条件が反転の関係にある
  • Exit For / Exit Do でループを途中で脱出できる
  • ネストしたループでは、内側の Exit For は最も内側のループのみを抜ける
  • 行の削除は逆順ループStep -1)で行うのが鉄則
  • 大量データの処理では、配列への一括読み込み画面更新の一時停止で大幅に高速化できる
  • カウンター変数には Integer ではなく Longを使用する
条件分岐 (If / Select Case)
VBAの条件分岐(If文、ElseIf、Select Case)の書き方を初心者向けに徹底解説。比較演算子や論理演算子の使い方、ネスト、If文とSelect Caseの使い分けの基準まで、実践的なコード例とともに紹介します。
Forループのスキップ(Continue)
VBAのForループ内で次のループへスキップする方法を紹介します。GoToコマンドを使うことで、Forループ内で次のループへスキップすることができます。
コレクション、配列の操作
VBA を使用して特定のコレクションや配列を効率的に処理することは、開発者にとって重要なスキルです。この記事では、VBA でコレクションや配列を 1 件ずつ処理する方法について詳しく解説します。
配列の操作
配列を使用することで、複数のデータを一つの変数に格納することができます。VBAにおける配列の定義と操作方法について解説します。
実行速度の改善
出来上がったものを実行してみると、処理中に画面がガタガタ動いたり、マウスが頻繁に処理中モードと通常モードに切り替わってしまうことはありませんか?そういった方向けに、作成した処理の前に読み込ませるだけで、簡単に上記の問題が解決され、さらに高速
#VBA #ループ処理 #For Next #For Each #Do While #Do Until #繰り返し処理 #制御構文 #初心者向け