🚀The world's best VBA AI has evolved. ExcelMaster is now an autonomous Agent.Read more →
Back to Blog

Excel VBA でファイルの存在を確認する — Dir 対 FileSystemObject.FileExists(そして Dir ループの中の罠)

|

Excel VBA でファイルの存在を確認する — Dir 対 FileSystemObject.FileExists(そして Dir ループの中の罠)

TL;DR — 正しいやり方は二つ、それぞれの道具に一つずつ。DirDir ループの近くに一切 いないときの 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 ループを動かすのと同じ隠れたカーソルを背負って いることです — だから、どう呼ぶかと同じくらい、どこで呼ぶかが効いてきます。

この記事で学べること

  • 間違った反射 — 「とにかく開いてエラーを捕まえる」 — が最悪の選択肢である理由
  • 二つの正しい答え — DirFileSystemObject.FileExists
  • この話題を決定づける罠 — Dir チェックが Dir ループをリセットする
  • Dirフォルダーパス末尾のバックスラッシュを取り違える様子
  • FileExistsFolderExists — 意図をぴったり言い表す
  • 「チェックしてから開く」になおエラーガードが必要な理由(小さな競合と、現実的な対処)

間違った反射:開いてエラーを捕まえる

魅力的な近道は、チェックを丸ごと飛ばして、失敗に教えてもらうことです。

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") <> "" ThenDir はファイルが 存在すれば名前を、なければ "" を返します。より明快で状態を持たないやり方は 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