目次
目的
- パソコン内に指定ファイルが存在しているかどうかを調べる。
下記のような場合は「再帰処理」を使用した方が良いかもしれません。
- 検索対象のフォルダが多数。
- サブフォルダ(フォルダの中のフォルダ)まで検索したい。
- ツリーのトップのフォルダ(ルートフォルダ)を指定して、ツリー全体を検索対象にしたい。
「vba 再帰処理 ファイル」「vba 再帰処理 fso」などのキーワードで検索すると目的の情報を得られると思います。
その一例
- パソコンのデスクトップの「folder1」フォルダに、いくつかのファイルが入っている状態でテストしてみます。
- 「folder1」に存在するファイルを検索してみます。
Sub 存在するファイルを検索()
Debug.Print """" & Dir("C:¥Users¥xxx¥デスクトップ¥folder1¥file1.txt") & """"
End Sub- 「Dir("C:¥Users¥xxx¥デスクトップ¥folder1¥file1.txt")」
Dir( 検索条件 ) の書式になっています。 - """"
文字の始まりと終わりが分かりやすいように、ダブルクォーテーション「"」で囲んでいます。
「"」二つで「"」を出力出来ます。「""」を「"」で囲んで「""""」としています。 - 実行結果は以下のようになりました。
- 「Dir("C:¥Users¥xxx¥デスクトップ¥folder1¥file1.txt")」
- 「folder1」に存在しないファイルを検索してみます。
Sub 存在しないファイルを検索()
Debug.Print """" & Dir("C:¥Users¥xxx¥デスクトップ¥folder1¥file0.txt") & """"
End Sub- 「file0.txt」
file0.txt は存在していません。ファイルが存在しない場合、長さ 0 の文字列が返ってくるようです。 - 実行結果は以下のようになりました。
- 「file0.txt」
- ワイルドカードで同じ拡張子のファイルを検索してみます。私が Dir 関数の動作をよく分かっていないので、その理解のために、Dir 関数を何回か実行してみます。
Sub Dir関数の動作理解のために同じ拡張子のファイルを検索()
Debug.Print """" & Dir("C:¥Users¥xxx¥デスクトップ¥folder1¥*.txt") & """" 'Dir 関数実行 1 回目
Debug.Print """" & Dir & """" '2 回目
Debug.Print """" & Dir & """" '3 回目
Debug.Print """" & Dir & """" '4 回目
Debug.Print """" & Dir & """" '5 回目 'エラー発生
Debug.Print """" & Dir & """" '6 回目
End Sub- 「Dir 関数実行 1 回目」
1 回目は検索条件を指定しています。 - 「*.txt」
ワイルドカードのアスタリスク「*」はその位置に複数の文字があることを表現しています。クエスチョンマーク「?」で何かの 1 文字を表現出来ます。 - 「2 回目」
同じ検索条件で再検索する場合は Dir のみで実行します。 - 「3 回目」
3 回目まではファイル名が出力されます。
- 「4 回目」
3 回目までで検索条件に一致する全てのファイルを取得したので、 4 回目は、長さ 0 の文字列が返ってきます。
- 「5 回目」
5 回目でエラーが発生してマクロが中断されました。
- 「Dir 関数実行 1 回目」
- ワイルドカード+ループ処理で同じ拡張子のファイルを検索してみます。
Sub ループ処理で同じ拡張子のファイルを検索()
Dim a
a = Dir("C:¥Users¥xxx¥デスクトップ¥folder1¥*.txt") '実行順 1
Do While a <> "" '2,6,10,14
Debug.Print """" & a & """" '3,7,11
a = Dir '4,8,12
Loop '5,9,13
End Sub '15- 「Do ... Loop」
私が苦手とする Do Loop ステートメントを使用していますが、 Dir 関数のヘルプに載っていた制御構造です。
- 「実行順 1 ... 15」
処理の順番です。実行順 12 で長さ 0 の文字列が返ってきます。そのまま 13 へ進みますが、 14 で「Do While a <> ""」の条件に一致しないので Do Loop を抜けます。 - 実行結果は以下のようになりました。
- 「Do ... Loop」
- ワイルドカードを「*.*」にしてフォルダ内の全てのファイルを検索してみます。
Sub ループ処理で全てのファイルを検索()
Dim a
a = Dir("C:¥Users¥xxx¥デスクトップ¥folder1¥*.*")
Do While a <> ""
Debug.Print """" & a & """"
a = Dir
Loop
End Sub- 実行結果は以下のようになりました。
- 実行結果は以下のようになりました。
フォルダを検索
Dir 関数はフォルダも検索できるようです。
- 上記で出てきた「folder1」の中に、いくつかのフォルダを追加してテストしてみます。
- Dir 関数の第二引数に「vbDirectory」を指定してみます。
Sub 第二引数にvbDirectoryを指定_フォルダを検索()
Dim a
a = Dir("C:¥Users¥xxx¥デスクトップ¥folder1¥folder2", vbDirectory)
Debug.Print """" & a & """"
End Sub- 「vbDirectory」
vbDirectory は VBA.VbFileAttribute に入っている定数のようです。値は「16」と定義されているようです。
- 実行結果は以下のようになりました。
- 第二引数指定なしの場合、長さ 0 の文字列が返ってきました。
- 「vbDirectory」
- ループ処理でフォルダを検索してみます。
Sub ループ処理_フォルダを検索()
Dim a
a = Dir("C:¥Users¥xxx¥デスクトップ¥folder1¥*", vbDirectory)
Do While a <> ""
Debug.Print """" & a & """"
a = Dir
Loop
End Sub- 実行結果は以下のようになりました。フォルダ名のみ取得されると思っていましたが、ファイル名、ピリオドの記号も取得されています。
- 上記結果のピリオド「.」は現在のフォルダ(カレントフォルダというらしいです)を示す表記のようです。ピリオド 2 つの「..」は親フォルダという意味のようです。
- 「folder1¥*」
「folder1¥」でも同じ結果になりました。
- 実行結果は以下のようになりました。フォルダ名のみ取得されると思っていましたが、ファイル名、ピリオドの記号も取得されています。
- フォルダ名のみ抽出する方法は、下記 Dir 関数のヘルプページに載っていました。
下記ページの使用例のコメント部分は英語になっていますが、 VBE で Dir 関数のヘルプを見ると日本語のコメントが付いていました。
- 私の場合、上記ヘルプページに載っている以外の方法を知らないので、ヘルプそのままのようになってしまいますが、良ければご覧ください。
Sub ループ処理_フォルダ名のみ抽出()
Dim a
a = Dir("C:¥Users¥xxx¥デスクトップ¥folder1¥", vbDirectory)
Do While a <> ""
If a <> "." And a <> ".." Then
If (GetAttr("C:¥Users¥xxx¥デスクトップ¥folder1¥" & a) And vbDirectory) = vbDirectory Then
Debug.Print """" & a & """"
End If
End If
a = Dir
Loop
End Sub- 「If a <> "." And a <> ".." Then」
現在のフォルダ「.」と親フォルダ「..」以外の場合。 - 「GetAttr() And vbDirectory) = vbDirectory」
フォルダの属性を取得して、定数 vbDirectory(=16)と一致しているか判定。 - 実行結果は以下のようになりました。
- 「If a <> "." And a <> ".." Then」
- GetAttr 関数のテストも行ってみました。まずはファイルの属性を取得してみます。
Sub GetAttr関数でファイルの属性取得()
Debug.Print GetAttr("C:¥Users¥xxx¥デスクトップ¥folder1¥file1.txt")
End Sub- 「GetAttr」
GetAttr 関数は「属性の値」の合計値を返すようです。「属性の値」は、フォルダ属性「16」、アーカイブ属性「32」のように定数で決まっているようです。
- GetAttr 関数 (Visual Basic for Applications) | Microsoft Learn
- https://learn.microsoft.com/ja-jp/office/vba/language/reference/user-interface-help/getattr-function
- 実行結果は以下のようになりました。
- 「GetAttr」
- 次はフォルダの属性を取得してみます。
Sub GetAttr関数でフォルダの属性取得()
Debug.Print GetAttr("C:¥Users¥xxx¥デスクトップ¥folder1¥folder2")
End Sub- 結果は以下のようになりました。
- 結果は以下のようになりました。
- And 演算子でビット単位の比較というものを行うと「16」が取得できるようです。私の場合、ビット単位の比較というのが分かっていません。
Sub And_vbDirectoryを付加_GetAttr関数でフォルダの属性取得()
Debug.Print GetAttr("C:¥Users¥xxx¥デスクトップ¥folder1¥folder2") And vbDirectory
End Sub- 実行結果は以下のようになりました。
- 「And vbDirectory」の部分を「And vbArchive」に変更すると以下の結果になりました。
- 実行結果は以下のようになりました。
テスト結果ストック
実行したマクロ
Private Sub 呼出し関数(path)
'vbNormal 0 標準
'vbReadOnly 1 読み取り専用
'vbHidden 2 隠しファイル
'vbSystem 4 システムファイル。Mac 使用不可
'vbDirectory 16 フォルダ
'vbArchive 32 前回バックアップ以降変更有。Mac 使用不可
'vbAlias 64 Mac のみ
Debug.Print "path="; path
Debug.Print "GetAttr="; GetAttr(path)
Debug.Print "vbNormal="; GetAttr(path) And vbNormal
Debug.Print "vbReadOnly="; GetAttr(path) And vbReadOnly
Debug.Print "vbHidden="; GetAttr(path) And vbHidden
Debug.Print "vbSystem="; GetAttr(path) And vbSystem
Debug.Print "vbDirectory="; GetAttr(path) And vbDirectory
Debug.Print "vbArchive="; GetAttr(path) And vbArchive
Debug.Print "vbAlias="; GetAttr(path) And vbAlias
End Sub
Sub GetAttr関数で属性を調べる()
Const a = "C:¥Users¥xxx¥デスクトップ¥folder10"
呼出し関数 (a & "¥ReadOnly-Hidden.txt")
呼出し関数 (a & "¥ReadOnly.txt")
呼出し関数 (a & "¥Normal.txt")
呼出し関数 (a & "¥Hidden.txt")
呼出し関数 (a & "¥Directory")
End Sub
実行結果を表にまとめたもの
path |
GetAttr |
vb Normal 定数=0 |
vb ReadOnly 定数=1 |
vb Hidden 定数=2 |
vb System 定数=4 |
vb Directory 定数=16 |
vb Archive 定数=32 |
vb Alias 定数=64 |
ReadOnly-Hidden.txt | 35 | 0 | 1 | 2 | 0 | 0 | 32 | 0 |
ReadOnly.txt | 33 | 0 | 1 | 0 | 0 | 0 | 32 | 0 |
Normal.txt | 32 | 0 | 0 | 0 | 0 | 0 | 32 | 0 |
Hidden.txt | 34 | 0 | 0 | 2 | 0 | 0 | 32 | 0 |
Directory | 16 | 0 | 0 | 0 | 0 | 16 | 0 | 0 |
「Directory」の GetAttr の結果が 16 になっています。上記ではフォルダの GetAttr の結果は 48 と出てきました。 48 を再現しようとしてみましたが、なぜか出来ないままです。
テスト環境
- Windows 10(64 ビット)
- Microsoft Office Excel 2003
以上、閲覧ありがとうございました。