Do Whileが終わらない原因と
終了条件
シートごとにデータの行数が違っても、空白セルが現れるまで処理を続けたい。便利なDo Whileを、無限ループにしないための考え方と一緒に整理します。
Do Whileを調べるきっかけは、実際に使ったときに処理が終わらなくなり、Excelを強制終了した結果、それまでに作成していた未保存のデータを失ってしまったことでした。
やりたかったこと自体は単純です。各シートを上から確認し、指定した列が空白になるまで一行ずつ処理します。シートによって最終行が毎回違う場合でも、同じコードを使えるようにするのが目的でした。
繰り返し処理は短時間で多くのセルを変更します。動作確認中は元ファイルを直接使わず、実行前に保存しておくと、停止に失敗したときの被害を抑えられます。
HOW TO READ
Do Whileの条件は「続ける条件」
最初に少し迷ったのが、不等号を含む条件式の読み方でした。Do Whileの後ろに書くのは「この条件になったら終了」ではなく、この条件が成立している間は繰り返すという条件です。
Do While Len(CStr(ws.Cells(currentRow, "A").Value)) > 0
'A列に値がある間、ここを繰り返す
currentRow = currentRow + 1
LoopLen(...) > 0は「文字数が0より大きい」、つまり対象セルが空白ではないという意味です。空白セルに到達すると条件が成立しなくなり、ループを抜けます。
Do While セル <> ""
条件がTrueの間だけ繰り返します。
Do Until セル = ""
条件がTrueになった時点で終了します。
Do Whileの条件は「終了条件」ではなく「継続条件」と読んだ方が理解しやすい。
INFINITE LOOP
終わらない主な原因
1. カウンタを加算していない
最も起こりやすいのは、現在行を表す変数を更新し忘れるパターンです。同じセルを何度確認しても条件は変わらないため、処理が永遠に続きます。
currentRow = 2
Do While ws.Cells(currentRow, "A").Value <> ""
'処理
'currentRowを加算していないため、同じ行を見続ける
LoopcurrentRow = currentRow + 12. 「=」の値をカウンタが飛び越える
「10ではない間」のように完全一致だけを終了条件にすると、加算幅によっては目的の値を通過し、二度と終了できなくなります。
counter = 1
Do While counter <> 10
counter = counter + 2
Loop
'1 → 3 → 5 → 7 → 9 → 11 …
'10と等しくならないため終了しない上限まで繰り返すなら、完全一致ではなく範囲で判定します。
Do While counter < 103. 終了条件になる空白が存在しない
対象列の最後まで値が入っている、確認する列を間違えている、想定外の数式や空白文字が入っている場合も、期待した位置では止まりません。コードだけでなく、終了条件になるデータが実際に存在するかも確認します。
DIFFERENT LENGTHS
シートごとに違う行数を処理する
各シートで最初の空白行が異なっていても、処理の開始時にcurrentRowを戻せば、同じDo Whileを繰り返し使えます。
| 対象シート | 最初の空白行 | 最後に処理する行 |
|---|---|---|
| シート1 | 100行目 | 99行目 |
| シート2 | 150行目 | 149行目 |
| シート3 | 80行目 | 79行目 |
シート1の処理が終わったら、シート2では再び開始行から確認します。最初に見つかった空白セルでそのシートのループを抜け、次のシートへ進みます。
SAFER SAMPLE
安全策を加えた全体コード
次の例では、すべてのワークシートについて、A列が空白になるまで一行ずつ確認します。実際の処理内容はDebug.Printの部分を置き換えて使用してください。
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 SubcurrentRow = START_ROWシートが変わるたびに、確認する行を開始位置へ戻す。
currentRow + 1一行処理するたびに、次の行へ確実に移動する。
MAX_REPEAT終了条件に不備があっても、指定回数でループを強制的に抜ける。
DoEvents長い処理中もExcelがキー操作を受け取りやすくする。終了条件そのものの代わりにはしない。
途中に空白行があるデータでは、その空白で処理が終了します。途中の空白を無視したい場合は、空白を終了条件にせず、最終行を取得してFor~Nextで回す方法が適しています。
IF IT DOES NOT STOP
無限ループに入ったときの対応
処理が終わらないと感じたら、まずEscまたはCtrl + Breakでマクロの中断を試します。中断できたら「デバッグ」を選び、黄色で示された行と、カウンタの現在値を確認します。
- 実行前にブックを保存し、できればコピーで試す
- 最初は一つのシート、少ない行数だけで動作確認する
- カウンタが毎回変化しているか確認する
- 想定回数を超えたら
Exit Doする安全装置を入れる - 強制終了は未保存データを失う可能性があるため、最後の手段にする
SUMMARY
終了条件と、条件を変化させる処理をセットで考える
Do Whileは、シートごとにデータの長さが違う処理でとても便利です。一方で、継続条件がいつまでもTrueのままだと処理は終わりません。
- Do Whileの後ろは「成立している間、続ける条件」
- 空白まで処理するなら、空白ではない間だけ繰り返す
- 行番号やカウンタを、ループ内で必ず更新する
- 完全一致だけに頼らず、飛び越しても終了できる比較にする
- 上限回数と中断しやすい仕組みを加えておく
「どうなったら終わるか」と「その状態へ近づく処理があるか」を実行前に確認するだけでも、無限ループの多くは防げます。