【VBAでフォルダ/ファイル操作】(3)すべての下位フォルダ内のファイルを操作する

ノートPC
この記事は約10分で読めます。

Excel-VBAでフォルダやファイルを操作する方法を3回の記事にわけて紹介します。

(1)FileSytemObjectを使用する準備
(2)フォルダ内にあるサブフォルダとファイルを操作する
(3)すべての下位フォルダ内のファイルを操作する   ― 今回の記事

前回まででFileSystemObjectを使用する準備とそれを使用してフォルダ内のサブフォルダとファイルを操作する方法を紹介しました。

今回はさらにサブフォルダ内も含めたすべての下位フォルダ内のファイルを操作する方法を紹介します。

最後に実用的なサンプルマクロも紹介します。

再帰呼び出し

前回の記事であるフォルダ内のサブフォルダおよびファイルを操作する方法を紹介しました。前回のサンプルコードをもう一度みてみましょう。

Sub sample3()
    Dim folderPath As String
    Dim fso As FileSystemObject
    Dim objFolder As Folder
    Dim objFile As File
    folderPath = "D:\sample"
    Set fso = New FileSystemObject
    '//フォルダ内のサブフォルダパスを取得
    For Each objFolder In fso.GetFolder(folderPath).SubFolders
        Debug.Print objFolder.Path
    Next
    '//フォルダ内のファイルパスを取得(自身以外のExcelブックを対象)
    For Each objFile In fso.GetFolder(folderPath).Files
        If InStr(objFile.Name, ".xls") > 0 Then
            If InStr(objFile.Name, ThisWorkbook.Name) = 0 Then
                Debug.Print objFile.Path
            End If
        End If
    Next
End Sub

今回はここで得られたサブフォルダの中にあるフォルダやファイル、さらにその下位のフォルダおよびファイルとすべてのファイルを操作する方法を紹介します。

ここで前回のサンプルコードでサブフォルダオブジェクトを得るためとファイルオブジェクトを得るための2つのFor Each..Nextステートメントをみてみましょう。

ここでコレクションオブジェクトを得るためにどちらも.GetFolderメソッドの引数に同じ変数(folderPath)を与えていることに注目してください。

この変数は対象としているフォルダのパスを格納した変数です。

言い換えればこのサンプルコードはこの変数で指定したフォルダ内のサブフォルダとファイルを操作するプロシージャと言えます。

つまりこの変数をサブフォルダのパスに変更すればサブフォルダ内のフォルダおよびファイルが操作できることになります。

そのサブフォルダのパスは1つ目のFor Each..Nextステートメントで objFolder.Path として得られています。これを引数としてこのプロシージャを実行すればいいということになります。

Sub sample3(folderPath As String)
 ・・・
 ・・・
 For Each objFolder In fso.GetFolder(folderPath).SubFolders
  Call sample3(objFolder.Path)
 Next
 ・・・

このようにそのプロシージャ内で自分自身を呼び出すことを「再帰呼び出し」と言います。

ただし1つ目のフォルダ(実行したい一番上位のフォルダ)は最初に指定する必要があるため、別途その上位フォルダを指定してこのプロシージャを呼び出すための別プロシージャを用意する必要があります。

Sub Main()
 Call sample3(“D:\sample”)
End Sub

すべての下位フォルダ内のファイルを操作する

それでは再帰呼び出しを利用して下位フォルダ内のすべてのファイルを操作するサンプルコードを作成してみましょう。

'//作業するフォルダを指定してマクロを実行
Sub Main()
    Call getFile("D:\sample")
End Sub

'//サブフォルダ含めてすべてのファイルを操作
Private Sub getFile(folderPath As String)
    Dim fso As FileSystemObject
    Set fso = New FileSystemObject
    Dim objFolder As Folder
    Dim objFile As File
    '//すべてのフォルダを操作
    For Each objFolder In fso.GetFolder(folderPath).SubFolders
        Call getFile(objFolder.Path)  '//サブフォルダを指定して再帰呼び出し
    Next
    '//フォルダ内のファイルパスを取得(自身以外のExcelブックを対象)
    For Each objFile In fso.GetFolder(folderPath).Files
        If InStr(objFile.Name, ".xls") > 0 Then
            If InStr(objFile.Name, ThisWorkbook.Name) = 0 Then
                Debug.Print objFile.Path
            End If
        End If
    Next
    Set fso = Nothing
End Sub

実行するのはMainプロシージャになります。ここで対象としたい上位のフォルダを指定し、すべてのファイルを操作するためのgetFileプロシージャを呼び出しています。

getFileプロシージャMainプロシージャからの呼び出し専用なので他のモジュールから呼び出しできないようにPrivate Subとしています。

1つ目のFor Each..Nextステートメントでサブフォルダのパスを指定して再帰呼び出しをしています。ここですべての下位フォルダを対象にしてgetFileプロシージャが実行されます。

再帰呼び出しされたプロシージャで更に下位フォルダを再帰呼び出ししますが対象となる下位フォルダがなくなるとつぎのファイルパスを取得するFor Each..Nextステートメントが実行されます。

どのような順番でコードが実行されるのかステップ実行で確認してみましょう。

ここではファイルオブジェクトを使ってファイルのパス名をイミディエイトウィンドウに表示させていますが、このファイルオブジェクトを使って実際にファイルに対して操作したい処理を実行させることができます。

実用的なサンプルマクロ

最後に実用的なサンプルマクロをひとつ紹介しておきます。

「D:\sample」フォルダのなかに複数のサブフォルダがありその中にも更にサブフォルダがあるとします。フォルダの数や階層数は不定です。それぞれのフォルダには入っている複数のデータ用のExcelブックがあります。

これらのデータを集計用のブックに取り込む事例です。

  • 集計用のブックにマクロを保存して実行します。
  • データはデータ用ブックのシート名「DATA」のA,B列にあります。
  • 1行目は見出し行で2行目以降にデータがあり、データ数は不定です。
  • 取り込むのは集計用ブックのシート名「集計」のA,B列です。
'//作業するフォルダを指定してマクロを実行
Sub Main()
    Application.ScreenUpdating = False
    Call getFile("D:\sample")
    Application.ScreenUpdating = True
    MsgBox "処理が完了しました", vbInformation
End Sub

'//サブフォルダ含めてすべてのファイルを操作
'///////////////////////////////////////////////////
'//参照設定「Microsoft Scripting Runtime」を適用//
'///////////////////////////////////////////////////
Private Sub getFile(folderPath As String)
    Dim fso As FileSystemObject
    Set fso = New FileSystemObject
    Dim objFolder As Folder
    Dim objFile As File
    '//すべてのフォルダを操作
    For Each objFolder In fso.GetFolder(folderPath).SubFolders
        Debug.Print objFolder.Name
        Call getFile(objFolder.Path)  '//サブフォルダを指定して再帰呼び出し
    Next
    '//フォルダ内のファイルを操作
    For Each objFile In fso.GetFolder(folderPath).Files
        If InStr(objFile.Path, ".xls") > 0 Then
            If InStr(objFile.Name, ThisWorkbook.Name) = 0 Then
                Call aggData(objFile.Path)
            End If
        End If
    Next
    Set fso = Nothing
End Sub

'//ファイル名を指定して開いたファイルのデータを取り込み
Private Sub aggData(filePath As String)
    Dim ws1 As Worksheet    '//集計用シート
    Dim inputRow As Long    '//集計シートの入力行
    Dim wb2 As Workbook     '//データブック
    Dim ws2 As Worksheet    '//データブックのデータシート
    Dim endRow As Long      '//データシートの最終行
    
    '//集計シートの入力行を取得
    Set ws1 = ThisWorkbook.Worksheets("集計")
    inputRow = ws1.Cells(Rows.Count, 1).End(xlUp).Row + 1
    '//データブックを開き、データシートの最終行を取得
    Set wb2 = Workbooks.Open(filePath)
    Set ws2 = wb2.Worksheets("DATA")
    endRow = ws2.Cells(Rows.Count, 1).End(xlUp).Row
    '//集計シートにデータを取り込み
    ws1.Cells(inputRow, 1).Resize(endRow - 1, 2).Value = _
        ws2.Range(ws2.Cells(2, 1), ws2.Cells(endRow, 2)).Value
    '//データブックを閉じる
    wb2.Close
End Sub

前項のサンプルコードからデータ取り込み用にaggDataプロシージャを追加しています。getFileプロシージャからファイルパスを引数として受け取って、そのファイルを開いて処理を実行しています。

この事例ではFileSystemObjectは事前バインディングによって生成しているので参照設定で「Microsoft Scripting Runtime」を設定しておく必要があります。

実行時バインディングで利用したい場合はgetFileプロシージャの変数の宣言およびオブジェクト作成を次のようにします。

Dim fso As Object
Set fso = CreateObject(“Scripting.FileSystemObject”)
Dim objFolder As Object
Dim objFile As Object

詳しくは1回目の記事を参照ください。

(1)FileSytemObjectを使用する準備
(2)フォルダ内にあるサブフォルダとファイルを操作する 
(3)すべての下位フォルダ内のファイルを操作する   ― 今回の記事

コメント

タイトルとURLをコピーしました