TL;DR — 正しいやり方は二つ、それぞれの道具に一つずつ。
DirはDirループの近くに一切 いないときの 1 行版。FileSystemObject.FileExistsは、隠れた状態を持たないので安全な既定です。
' 組み込み、1 行 — ただし Dir の単一グローバルカーソルを共有する
If Dir("C:\Reports\March.xlsx") <> "" Then MsgBox "It's there"
' 状態なし、より明快、ファイルとフォルダーを区別 — より安全な既定
Dim fso As Object: Set fso = CreateObject("Scripting.FileSystemObject")
If fso.FileExists("C:\Reports\March.xlsx") Then MsgBox "It's there"
「このファイルは存在するか?」は、VBA でいちばん検索されるファイル関連の問いです。というのも
その裏返し — 存在しないファイルを開くこと — は実行時エラー 1004 を発生させ、無人マクロを
その場で止めてしまうからです。よい知らせは、チェックが 1 行で済むこと。落とし穴は、その当たり前
に見える1 行、Dir が、Dir ループを動かすのと同じ隠れたカーソルを背負って
いることです — だから、どう呼ぶかと同じくらい、どこで呼ぶかが効いてきます。
この記事で学べること
- 間違った反射 — 「とにかく開いてエラーを捕まえる」 — が最悪の選択肢である理由
- 二つの正しい答え —
DirとFileSystemObject.FileExists - この話題を決定づける罠 —
DirチェックがDirループをリセットする Dirがフォルダーパスや末尾のバックスラッシュを取り違える様子FileExists対FolderExists— 意図をぴったり言い表す- 「チェックしてから開く」になおエラーガードが必要な理由(小さな競合と、現実的な対処)
間違った反射:開いてエラーを捕まえる
魅力的な近道は、チェックを丸ごと飛ばして、失敗に教えてもらうことです。
On Error Resume Next
Set wb = Workbooks.Open(path) ' なければ 1004 が発生し、握りつぶされる
On Error GoTo 0
If wb Is Nothing Then MsgBox "Not found"
これは動きますが、3 つの選択肢の中で最悪です。遅い(Excel は実際にファイルを開こうとする)、
無関係なエラーを握りつぶす(破損したファイル、アクセス権の問題、ロックされたファイルが、
すべて「見つからない」と同じ見た目になる)、そしてスイッチを入れっぱなしにするとバグを隠す
On Error Resume Next に寄りかかっている。存在のテストは安く、正確です。
それを見送ってエラーハンドラーに頼るのは、きれいな答えを濁った答えに交換することです。まず
チェックしましょう。
二つの正しい答え
Dir — 組み込みの 1 行。 ワイルドカードなしなら、Dir は 1 つのパスを試すだけで、そこに
あればファイル名を、なければ "" を返します。
If Dir("C:\Reports\March.xlsx") <> "" Then ' "" は見つからないという意味
短く、参照設定なし、オブジェクトなし。唯一の弱点は共有カーソルです(次節)。
FileSystemObject.FileExists — 状態を持たない既定。 True/False を返し、グローバルな状態に
一切触れない、真の述語です。
Dim fso As Object: Set fso = CreateObject("Scripting.FileSystemObject")
If fso.FileExists("C:\Reports\March.xlsx") Then ...
やることがそのまま読め、ファイルとフォルダーを区別し、そして — これがより安全な既定である理由 —
Dir ループの真ん中も含めてどこでも、副作用なしに呼べます。遅延
バインディング(CreateObject)を使えばどのマシンでも動きます。VBA FileSystemObject
を参照してください。
この話題を決定づける落とし穴:Dir ループの中の Dir チェック
これこそ、二つのチェックが交換可能でない理由であり、Dirを支配するのと
同じ隠れたカーソルの挙動です。Dir はひとつのモジュール全体の検索位置を保持します。ファイルを
テストしようと Dir(path) を呼ぶと新しい検索が始まり — あなたの列挙ループが頼りにしていた
位置を消してしまいます。
name = Dir(folder & "*.xlsx")
Do While name <> ""
If Dir(folder & "done\" & name) = "" Then ' ← カーソルをリセットする — ループが壊れる
Process folder & name
End If
name = Dir() ' これは *.xlsx ではなく "done\" の検索を進めてしまう
Loop
内側の Dir がエンジンを再度仕込むので、ループはファイルを飛ばしたり、繰り返したり、決して
終わらなかったりします。ルールは絶対です。Dir ループの中では、別のファイルを調べるために
Dir を決して呼ばない — 代わりに fso.FileExists を使う。こちらには乱すべきカーソルが
ありません。この一点の相互作用こそ、「どの存在チェックか?」が好みの問題ではない理由です。
呼び出し経路のどこかに Dir の列挙があるなら、答えは FileExists です。
Dir がフォルダーと末尾のバックスラッシュを取り違える
ループの外でも、Dir には FileExists が避ける 2 つのエッジケースがあります。Dir はファイル
についてきれいにイエス/ノーを答えるためではなく、名前に一致するために作られているからです。
- フォルダーパス。
Dir("C:\Reports")は"Reports"を返しうる — フォルダーに一致する — ので、「ファイルが存在する」テストがディレクトリに対して通ってしまいます。fso.FileExists("C:\Reports")は正しくFalseを返し(それはフォルダーだから)、fso.FolderExistsがフォルダーの問いを別に答えます。 - 末尾のバックスラッシュ。
Dir("C:\Reports\")は、その中のファイルではなくフォルダーの 存在をテストします。パスを連結で組み立てて区切りを 1 つ余らせると、簡単に踏みます。
既定では Dir は隠しファイルとシステムファイルも飛ばすので、本当に存在するのに隠されている
ファイルは、vbHidden 属性を渡さない限り「見つからない」と報告されます。FileExists にはこうした
あいまいさが一切ありません — 「このパスちょうどにファイルがあるか」を、それだけ答えます。
チェックしてから開く:小さな競合と現実的な対処
正直な注意点がひとつ。チェックする瞬間と開く瞬間のあいだに、別のプロセスがファイルを削除したり ロックしたりするかもしれません。実際にはこの「チェック時点から使用時点まで(time-of-check to time-of-use)」の隙間はごく小さく、めったに問題になりません — しかし正しいパターンは、チェックか エラーガードかを選ぶことではなく、両方を使うことです。
If Not fso.FileExists(path) Then
MsgBox "File not found: " & path ' 想定された、よくあるケース — きれいなメッセージ
Exit Sub
End If
On Error GoTo OpenFailed ' まれなケース — ロック、破損、アクセス権
Set wb = Workbooks.Open(path)
存在チェックが、ありふれた「そこにない」ケースを、クラッシュではなく明快なメッセージでさばきます。
エラーハンドラーが、チェックには予測できない本当に例外的な失敗を捕まえます。
これが、自動ジョブにおけるあらゆる Workbooks.Open のための堅牢な形です。
関連:ブックはすでに開いているか?
「ファイルが存在するか」の近い親戚が「Excel ですでに開いているか」で、これは別の問いであり、
別の答えを持ちます — ディスクには触れず、Workbooks を名前でループするのです。
Function IsOpen(fileName As String) As Boolean
Dim wb As Workbook
On Error Resume Next
Set wb = Workbooks(fileName) ' ファイル名で、例 "March.xlsx"
On Error GoTo 0
IsOpen = Not wb Is Nothing
End Function
存在はディスクについての話。「すでに開いている」はメモリ上の Workbooks コレクションについての
話です。すでに開いているファイルを開いても再読み込みはされません — Excel はメモリ上のコピーを
アクティブにするだけ — なので、二度走るかもしれないバッチマクロは、
Workbooks.Open を呼ぶ前にこれをテストすべきです。
正直な結論:どのチェックを、いつ
fso.FileExistsを既定にする。 状態を持たず、明快に読め、ファイルとフォルダーを区別し、 そして — 決定的な点 —Dirループが絡むならただ一つの安全なチェックです。Dirの 1 行版を使うのは、Dirの列挙が走っておらず、準備をゼロにしたい、手早く独立した テストのときだけ。- 裸の open-and-trap でチェックを置き換えない。 遅く、本当のエラーを隠します。
- チェックを、open のまわりのエラーガードと組にする — よくあるケースには チェックを、まれなケースにはハンドラーを。
これを正しくやれば、ナンバーワンのファイルクラッシュ — そこにないパスに対する 1004 — は、無人 マクロがログに残して先へ進める、きれいな 1 行メッセージになります。
ExcelMaster の活用
存在チェックは、簡単でない部分に当たるまでは簡単に見えます — はぐれたチェックがリセットする
Dir カーソル、ファイルテストが誤って一致するフォルダーパス、なおエラーガードを必要とさせる
競合。バッチマクロが触れる一つひとつのファイルに対してそのすべてを正しくやるのは、注意深い
配管の積み重ねです。
ExcelMaster が、
それをこなします。ジョブを説明すれば — 「期待される各レポートについて、ファイルがあれば取り込み、
なければ欠落としてログに残して進んで」 — フォルダーループを決して乱さない
fso.FileExists チェックを書き、各 Workbooks.Open を
エラーハンドラーと組にし、開き直す前にブックがすでに開いているかを
テストします。あなたは結果を説明するだけ。チェックを正しく、副作用なしにするのは、それがやります。
よくある質問
VBA でファイルの存在を確認するには?
よい方法が二つ。組み込みの 1 行版は If Dir("C:\path\file.xlsx") <> "" Then — Dir はファイルが
存在すれば名前を、なければ "" を返します。より明快で状態を持たないやり方は
CreateObject("Scripting.FileSystemObject").FileExists(path) で、True/False を返し、どこでも
呼べます。FileExists を選び、Dir ループの中では決して Dir でテストしない
ようにしましょう。
Dir のファイルチェックが Dir ループを壊すのはなぜですか?
Dir が単一の隠れた検索カーソルを保持しているからです。ファイルをテストしようと Dir(otherPath)
を呼ぶと新しい検索が始まり、あなたの列挙ループが依存している位置を消してしまうので、ループは
その後ファイルを飛ばしたり、繰り返したり、永遠に回ったりします。Dir ループの中では、別の
ファイルを調べるのに fso.FileExists を使いましょう — 共有状態がありません。
ファイルの存在を調べるのに Dir と FileSystemObject のどちらを使うべきですか?
FileSystemObject.FileExists を既定にしましょう — 状態を持たず、ファイルとフォルダーを区別し、
Dir ループの中でも安全に呼べます。Dir の 1 行版は、Dir の列挙が走っておらず準備を要らなく
したい、手早く独立したチェックのときだけ使いましょう。VBA FileSystemObject
を参照してください。
Dir はフォルダーに対して true を返しますか?
返しうります。Dir("C:\Reports") は "Reports" を返してフォルダーに一致することがあり、素朴な
「ファイルが存在する」テストがディレクトリに対して通ってしまいます。そして末尾のバックスラッシュ
つきの Dir("C:\Reports\") は、その中のファイルではなくフォルダーをテストします。
FileSystemObject.FileExists は、ファイルについて厳密に答えることでどちらも避け、フォルダーの
問いには FolderExists を用意しています。
VBA でブックがすでに開いているかを確認するには?
それは別の問いです — ディスクではなくメモリについての話です。Workbooks コレクションをファイル
名でループします:On Error Resume Next ブロックの中で Set wb = Workbooks("March.xlsx")、それから
Not wb Is Nothing をテストします。すでに開いているファイルを開いても再読み込みはされないので、
二度走るかもしれないマクロは、Workbooks.Open を呼ぶ前にこれを
確認すべきです。
検証環境
検証環境: Excel 365 (Windows 11), VBA 7.1 — 最終確認 2026-08-21。
関連ガイド: VBA Dir · VBA FileSystemObject · VBA Open Workbook · VBA On Error · VBA Close Workbook
