[Excel]VBA ファイルやフォルダの存在を確認する

ファイルやフォルダを操作するマクロを実行するとき、指定したファイルやフォルダがないと困るケースがあります。また、ファイルやフォルダの有無によって処理を分岐させたいケースもあると思います。

処理の事前にファイルやフォルダの存在チェックをする事でエラーを防いだり、処理を分岐させる事ができます。

目次

Dir関数

構文

Dir(pathname(省略可能),[attributes(省略可能)])

引数 pathname

Dir関数は、引数pathnameに指定したパスから、最初のファイル名パスを含まないファイル名のみ)を返します。一致するファイル名が無い場合は長さ0の文字列「””」を返します。

Public Sub Dir関数サンプル1()
    Dim ファイル名 As String
    
    ファイル名 = Dir("C:\Users\YOU\Documents\test.xlsx")
    
    If ファイル名 <> "" Then
        MsgBox (ファイル名)
    Else
        MsgBox ("ファイルが存在しません")
    End If
End Sub

サンプルはファイルが存在した場合、パスを含まないファイル名を表示します。存在しない場合は「””」を返すので「ファイルが存在しません」が表示されます。

要は指定したパスが存在しなくてもエラーにはなりません。但し、pathname空白「” “」を指定するとエラーになります。

ワイルドカード

pathnameにはワイルドカードを指定できます(Macは不可)。複数文字は「*」、一文字は「?」で指定します。前述の最初のファイル名とはワイルドカードで指定した場合、「一致するファイルが複数あったときに最初に抽出されたファイル名」の意味になります。

Dir関数を一度実行した後であればpathnameは省略可能です。Dir()で前回と同じ引数で再検索し、一致した次のファイル名を返します。但し、前回の実行で一致するファイルが無かった場合(長さ0の文字列「””」を返した直後)、あるいは一度目の実行でpathnameを省略した場合はエラーになります。

Public Sub Dir関数サンプル2()
Dim ファイル名 As String
    
    ファイル名 = Dir("C:\Users\YOU\Documents\*.xlsx")
    
    Do While ファイル名 <> ""
        MsgBox (ファイル名)
        ファイル名 = Dir()
    Loop
End Sub

サンプルはドキュメントフォルダの拡張子xlsxファイルを全て表示します。「一致するファイル名が無い場合は長さ0の文字列「””」を返す」をDoLoopステートメントの条件にします。

3桁拡張子の罠

ワイルドカードで指定するとき「*.xls」のように3桁拡張子を指定すると、前方一致の4桁拡張子もHITします(xlsxxlsm等)。どうもWindowsの仕様らしいですが、3桁拡張子でパスを指定する場合は注意が必要です。回避方法はググると出てきます。

パス長の制限(があるらしい)

そもそもWindowsにはパス長の制限があるようです(Windows10の場合、フォルダパスで247文字、ファイル名含むフルパスで259文字らしい)。テストで長いフォルダ名にしてみたところ、一定の文字数(文字数は確認していません)を超えると「フォルダ名が長すぎる」とWindowsに怒られだします。

怒られないギリギリの文字数でマクロを実行してもVBAには怒られませんでした。

他サイトを調べる限りでは、パス制限について言及しているサイトのほとんどが「Dir関数(のpathname)には256バイト制限がある」と書いています。

私はWindowsに怒られずにVBAで制限にひっかかるケースを再現できていないので、この制限を情報として頭の片隅に置いています。

まぁきっと、この引用部分が答えなんでしょうね。

256バイトを超えるパス名が扱えない

これは、そもそもWindowsとしての制限もあるので、ローカルのPC内の処理では通常は問題が出ないはずです。

https://excel-ubara.com/excelvba4/EXCEL262.html

引数 attributes

attributesには次の属性が指定できます。

vbNormal

(規定値)属性のないファイル。「読み取り専用」でも「隠しファイル」でもない標準ファイルです。

vbReadOnly

標準ファイルと読み取り専用ファイル。ファイルのプロパティで設定する「読み取り専用」にチェックが入ったファイルです。

vbHidden

標準ファイルと隠しファイル。プロパティの「隠しファイル」にチェックが入ったファイルです。

vbSystem

標準いファイルとシステムファイル。OSにまつわるファイルです。

vbVolume

標準ファイルとボリュームラベル。ストレージの記憶領域やメディアに付けられた名前です。

vbDirectory

標準ファイルとフォルダ。

省略するとvbNormalになります。どの属性を指定しても「標準ファイル」は必ずセットで指定されます。

Public Sub Dir関数サンプル3()
    Dim ファイル名 As String
    
    ファイル名 = Dir("C:\Users\YOU\Documents\", vbHidden)
    
    Do While ファイル名 <> ""
        MsgBox (ファイル名)
        ファイル名 = Dir()
    Loop
End Sub

C:\Users\YOU\Documents\のようにパスの最後に「\」を付けるとそのフォルダ内のファイル全てが取得できます。

サンプルを実行すると、ドキュメントフォルダの標準ファイルと隠しファイルが表示されます。が、使い勝手悪いです。ここを指定する人はきっと、属性別にファイル操作したい人のハズです。なぜ標準ファイルがもれなく付いてくるのでしょうか?

また、私の環境ではvbNormalを指定しても「読み取り専用」ファイルが一緒に抽出されます。「隠しファイル」はちゃんと除外されるのですが…

読み取り専用ファイルや隠しファイル「のみ」抽出したい方は、GetAttr関数(pathnameを返す関数)でググって下さい。

サンプルコード

ファイルの存在をチェックするコード

Public ファイルチェック(ByVal ファイル_パス As String) As Boolean
    Dim ダイアログ As Long

    If Dir(ファイル_パス) <> "" Then
        ダイアログ = MsgBox("ファイルが既に存在します。" & vbCrLf & "〇〇しますか?", vbYesNo)
        Select Case ダイアログ
        Case vbYes
            Exit Function
        Case vbNo
            MsgBox "処理を中止しました"
            ファイルチェック = True
        End Select
    End If
End Function

Public Sub デモ1()
    If ファイルチェック("C:\Users\YOU\Documents\test.xlsx") Then Exit Sub
    MsgBox "〇〇しました"  'Yesの処理を記述
End Sub

ファイルチェックするコードはTrueを返す関数にし、別プロシージャで呼び出す形にしています。

デモ1の実行前にファイルの存在を確認、存在する場合はデモ1を実行する/しないの2択、存在しなければそのままデモ1を実行するコードです。

プロシージャを分割しない場合はこのコードでおおよそ同じ挙動になります。

Public Sub デモ2()
    Dim ダイアログ As Long

    If Dir("C:\Users\YOU\Documents\test.xlsx") <> "" Then
        ダイアログ  = MsgBox("ファイルが既に存在します。" & vbCrLf & "〇〇しますか?", vbYesNo)
    
        Select Case ダイアログ
        Case vbYes
            MsgBox "〇〇しました"
        Case vbNo
            MsgBox "処理を中止しました"
        End Select
    Else
        MsgBox "〇〇しました"
    End If
End Sub

フォルダの存在をチェックするコード

引数attributesvbDirectoryを指定します。

サンプルは、ドキュメントフォルダ直下のTESTフォルダ有無を確認、無ければフォルダを作成する/しないの2択、そもそも存在する場合は処理が何も発生しないコードです。

Public Function フォルダチェック(ByVal フォルダ_パス As String) As Boolean
    Dim ダイアログ As Long

    If Dir(フォルダ_パス, vbDirectory) = "" Then
        ダイアログ = MsgBox("保存用フォルダ「" & フォルダ_パス & "」がありません。作成しますか?", vbYesNo)
        Select Case ダイアログ
        Case 6
            MkDir フォルダ_パス 'MkDirステートメント…フォルダ作成
            MsgBox "フォルダ「" & フォルダ_パス & "」を作成しました"
            Exit Function
        Case 7
            MsgBox "処理を中止します"
            フォルダチェック = True
        End Select
    End If
End Function

Public Sub デモ3()
    If フォルダチェック("C:\Users\YOU\Documents\TEST") Then Exit Sub
End Sub

「ファイルの存在をチェックするコード」と同様で関数化していますが、少し挙動が違います。

「ファイルの存在をチェックするコード」はファイルが有る時は2択、無いときは処理を実行します。「フォルダの存在をチェックするコード」はフォルダが無い時は2択、有るときは処理が何もないコードです。関数内に処理を含めるかどうかで挙動を変えることができます。

言い換えると、「ファイルの存在をチェックするコード」はチェックするだけのプロシージャです。「フォルダの存在をチェックするコード」はチェックして処理もするプロシージャです。

メインのプロシージャで関数(チェックするコード)以降の処理がある場合、このような書き分けが出来ると便利です。

Dir関数を使ったファイルやフォルダの存在確認方法を紹介しました。ここでは紹介していませんが、FileSystemObjectオブジェクト(Microsoft Scripting Runtimeライブラリのクラスです)を使用しても同様の処理ができますし、より高度な処理も可能です。

Dir関数は使うな、FileSystemObjectオブジェクト使え」といった意見が散見されます。それを見て私も一時期迷っていたのですが結局、Dir関数も使っています。

ガチのシステム屋さんにとっては、目の敵にする理由があるのかもしれませんが、少なくとも私のレベル(自分や身の回りの人の業務を自動化する位)では困ることはありません。

私と同じ境遇の人は、Dir関数と紹介したサンプルを是非使ってみて下さい。

それでは最後までご高覧いただきありがとうございました。

目次
閉じる