VBA実務メモ

Do Whileが終わらない原因と
終了条件

シートごとにデータの行数が違っても、空白セルが現れるまで処理を続けたい。便利なDo Whileを、無限ループにしないための考え方と一緒に整理します。

Do Whileを調べるきっかけは、実際に使ったときに処理が終わらなくなり、Excelを強制終了した結果、それまでに作成していた未保存のデータを失ってしまったことでした。

やりたかったこと自体は単純です。各シートを上から確認し、指定した列が空白になるまで一行ずつ処理します。シートによって最終行が毎回違う場合でも、同じコードを使えるようにするのが目的でした。

最初は必ずコピーしたブックで試す

繰り返し処理は短時間で多くのセルを変更します。動作確認中は元ファイルを直接使わず、実行前に保存しておくと、停止に失敗したときの被害を抑えられます。

HOW TO READ

Do Whileの条件は「続ける条件」

最初に少し迷ったのが、不等号を含む条件式の読み方でした。Do Whileの後ろに書くのは「この条件になったら終了」ではなく、この条件が成立している間は繰り返すという条件です。

VBA
Do While Len(CStr(ws.Cells(currentRow, "A").Value)) > 0
    'A列に値がある間、ここを繰り返す
    currentRow = currentRow + 1
Loop

Len(...) > 0は「文字数が0より大きい」、つまり対象セルが空白ではないという意味です。空白セルに到達すると条件が成立しなくなり、ループを抜けます。

Do While空白ではない間、続ける

Do While セル <> ""
条件がTrueの間だけ繰り返します。

Do Until空白になるまで、続ける

Do Until セル = ""
条件がTrueになった時点で終了します。

Do Whileの条件は「終了条件」ではなく「継続条件」と読んだ方が理解しやすい。

INFINITE LOOP

終わらない主な原因

1. カウンタを加算していない

最も起こりやすいのは、現在行を表す変数を更新し忘れるパターンです。同じセルを何度確認しても条件は変わらないため、処理が永遠に続きます。

VBA / 問題のある例実行注意
currentRow = 2

Do While ws.Cells(currentRow, "A").Value <> ""
    '処理
    'currentRowを加算していないため、同じ行を見続ける
Loop
VBA / 修正
currentRow = currentRow + 1

2. 「=」の値をカウンタが飛び越える

「10ではない間」のように完全一致だけを終了条件にすると、加算幅によっては目的の値を通過し、二度と終了できなくなります。

VBA / 10を飛び越える例実行注意
counter = 1

Do While counter <> 10
    counter = counter + 2
Loop

'1 → 3 → 5 → 7 → 9 → 11 …
'10と等しくならないため終了しない

上限まで繰り返すなら、完全一致ではなく範囲で判定します。

VBA / 修正
Do While counter < 10

3. 終了条件になる空白が存在しない

対象列の最後まで値が入っている、確認する列を間違えている、想定外の数式や空白文字が入っている場合も、期待した位置では止まりません。コードだけでなく、終了条件になるデータが実際に存在するかも確認します。

DIFFERENT LENGTHS

シートごとに違う行数を処理する

各シートで最初の空白行が異なっていても、処理の開始時にcurrentRowを戻せば、同じDo Whileを繰り返し使えます。

対象シート最初の空白行最後に処理する行
シート1100行目99行目
シート2150行目149行目
シート380行目79行目

シート1の処理が終わったら、シート2では再び開始行から確認します。最初に見つかった空白セルでそのシートのループを抜け、次のシートへ進みます。

SAFER SAMPLE

安全策を加えた全体コード

次の例では、すべてのワークシートについて、A列が空白になるまで一行ずつ確認します。実際の処理内容はDebug.Printの部分を置き換えて使用してください。

VBA
Option Explicit

Sub RepeatUntilBlank()

    Const START_ROW As Long = 2
    Const CHECK_COLUMN As String = "A"
    Const MAX_REPEAT As Long = 10000

    Dim ws As Worksheet
    Dim currentRow As Long
    Dim repeatCount As Long

    For Each ws In ThisWorkbook.Worksheets

        currentRow = START_ROW
        repeatCount = 0

        Do While Len(CStr(ws.Cells(currentRow, _
                          CHECK_COLUMN).Value)) > 0

            '実際の1行分の処理に置き換える
            Debug.Print ws.Name, currentRow, _
                        ws.Cells(currentRow, CHECK_COLUMN).Value

            currentRow = currentRow + 1
            repeatCount = repeatCount + 1

            '想定回数を超えた場合の安全停止
            If repeatCount >= MAX_REPEAT Then
                MsgBox ws.Name & ":処理回数が上限に達しました。", _
                       vbExclamation
                Exit Do
            End If

            'Excelがキー操作を受け取れるようにする
            DoEvents

        Loop

    Next ws

End Sub
currentRow = START_ROW

シートが変わるたびに、確認する行を開始位置へ戻す。

currentRow + 1

一行処理するたびに、次の行へ確実に移動する。

MAX_REPEAT

終了条件に不備があっても、指定回数でループを強制的に抜ける。

DoEvents

長い処理中もExcelがキー操作を受け取りやすくする。終了条件そのものの代わりにはしない。

i

途中に空白行があるデータでは、その空白で処理が終了します。途中の空白を無視したい場合は、空白を終了条件にせず、最終行を取得してFor~Nextで回す方法が適しています。

IF IT DOES NOT STOP

無限ループに入ったときの対応

処理が終わらないと感じたら、まずEscまたはCtrl + Breakでマクロの中断を試します。中断できたら「デバッグ」を選び、黄色で示された行と、カウンタの現在値を確認します。

  • 実行前にブックを保存し、できればコピーで試す
  • 最初は一つのシート、少ない行数だけで動作確認する
  • カウンタが毎回変化しているか確認する
  • 想定回数を超えたらExit Doする安全装置を入れる
  • 強制終了は未保存データを失う可能性があるため、最後の手段にする

SUMMARY

終了条件と、条件を変化させる処理をセットで考える

Do Whileは、シートごとにデータの長さが違う処理でとても便利です。一方で、継続条件がいつまでもTrueのままだと処理は終わりません。

  • Do Whileの後ろは「成立している間、続ける条件」
  • 空白まで処理するなら、空白ではない間だけ繰り返す
  • 行番号やカウンタを、ループ内で必ず更新する
  • 完全一致だけに頼らず、飛び越しても終了できる比較にする
  • 上限回数と中断しやすい仕組みを加えておく

「どうなったら終わるか」と「その状態へ近づく処理があるか」を実行前に確認するだけでも、無限ループの多くは防げます。

VBA・Excel自動化の記事