フォルダ内のファイル一覧

中級VBA

フォルダ内のファイル一覧

 ExcelVBAでフォルダ内のファイル一覧を取得するプログラムを紹介します。

この手のプログラムは様々な場面で利用可能なので、ある程度決まった形を覚えておくと使い回しに便利です。ここでは、なるべく単純な方法を取り上げていきたいと思います。

ファイル一覧を取得するのによく使われる方法

 ExcelVBAでフォルダ内のファイル一覧を取得するには、Dir関数を使用する方法と、FileSystemObjectを使用する方法があります。機能的にはFileSystemObjectの方が高機能です。

Dir関数を使った例

 Dir関数は最初に起点となるフォルダ+ファイルの種類を指定すると、該当するファイ名を返してきてくれます。

 ファイルの種類は、”\*.*” とすれば、全てのファイル、”\*.xlsx” などとExcelの拡張子を指定してやればExcelのファイルのみが検索対象になります。
 ファイルがない場合は空白を返すので、Do While から抜けることになります。

'Dir関数を使ってフォルダ内のファイル一覧
Sub searchFileForDir()
    Dim fname As String
    Dim myPath As String
    
    myPath = "C:\tools"    'フォルダを指定
    
    fname = Dir(myPath & "\*.*") 'ファイルの種類を指定
    
    Do While fname <> ""
            Debug.Print fname   'ファイル名のみ取り出せる
            'Debug.Print myPath & "\" & fname    'フルパス
        fname = Dir()
    Loop

End Sub

 Dir関数が返してくれるのはファイル名なので、パスを付けたい場合は、

 myPath & “\” & fname

のように、パス名と “\” をつなげてやります。”\” を忘れないように注意しましょう。

FileSystemObjectを使った例

 FileSystemObjectを使う場合は、CreateObject で呼び出してやる必要があります。
 変数で受ける場合は、Object型やVariant型を使います。


 FSO.GetFolder(myPath).Files を For Each に渡すとコレクションを返すので、それを今回は、myFile という Object型の変数で受けています。これもVariant型にしても構いません。

 myFileはそのままだとフルパスのファイル名を返してくれます。ファイル名のみ取得したい場合は、myFile.Name とします。

'FileSystemObjectを使ってフォルダ内のファイル一覧
Sub searchFileForFSO()
    Dim FSO As Object
    Dim myFile As Object
    Dim myPath As String

    myPath = "C:\tools\"
    
    Set FSO = CreateObject("Scripting.FileSystemObject")
    
    For Each myFile In FSO.GetFolder(myPath).Files
        Debug.Print myFile      'フルパス名が取り出せる
        'Debug.Print myFile.Name     'ファイル名だけ取り出す
    Next myFile
    
    Set FSO = Nothing
End Sub

 FileSystemObjectには、ファイル名だけでなく、その他ファイルやフォルダに関する色々な情報を取り出すことが出来ます。
 そのため少々複雑な事をするのであれば、選択肢はFileSystemObjectを使った方法の方が圧倒的に有利です。

'FileSystemObjectを使えば高機能なプロパティやメソッドが用意されている
Sub searchFileForFSO_ver2()
    Dim FSO As Object
    Dim myFile As Object
    Dim myPath As String
    Dim fullPath As String
    
    myPath = "C:\tools\"
    
    Set FSO = CreateObject("Scripting.FileSystemObject")
    'フォルダ内のファイル数
    Debug.Print FSO.GetFolder(myPath).Files.Count       

    For Each myFile In FSO.GetFolder(myPath).Files
        Debug.Print myFile   'フルパス
        Debug.Print myFile.Name  'ファイル名のみ
        Debug.Print FSO.GetParentFolderName(myFile) 'パスのみ
        Debug.Print FSO.GetBaseName(myFile) '拡張子なしファイル名
        Debug.Print FSO.GetExtensionName(myFile) '拡張子のみ
    Next myFile
    
    Set FSO = Nothing
End Sub

サブフォルダ内ファイルも取得する

 サブフォルダ内のファイルも取得したい場合は、処理は複雑になります。手法としては再帰呼び出しという方法を用いるのが一般的です。
 再帰呼び出しとは、ある関数の中で自分自身(関数)を呼び出すという方法です。この方法は少々トリッキーなので、プログラミングを誤ると無限ループになったり、エラーで止まったりするので注意が必要です。
 ただし、上手く使えばコード量の少ない効率的なプログラムを書くことができます。

'FileSystemObjectを使ったサブフォルダ内も探す方法
Sub searchAllFileForFSO(ByVal myPath As String)
    Dim FSO As Object
    Dim myFile As Object
    Dim myFolder As Object
    
    Set FSO = CreateObject("Scripting.FileSystemObject")
    
    For Each myFile In FSO.GetFolder(myPath).Files
        Debug.Print myFile
    Next myFile
    
    'サブフォルダを探す(再帰処理)
    For Each myFolder In FSO.GetFolder(myPath).SubFolders
        Call searchAllFileForFSO(myFolder)  'ここで自分自身を呼び出す
    Next myFolder
    
    Set FSO = Nothing
End Sub

'呼び出す関数
Sub Macro()
    Dim myPath As String
    myPath = "C:\tools"
    Call searchAllFileForFSO(myPath)
End Sub

 まず今回は関数が2つになります。再帰呼び出しを行う関数(searchAllFileForFSO)は、呼び出すときに引数を使いますので、単体では呼び出せません。そこで、別の関数から、起点となるフォルダ名を引数に指定しています。

 最初の引数をもらった searchAllFileForFSO関数は、FileSystemObjectを生成した後にそのフォルダ内のファイル一覧を取得します。
 尚、引数は ByVal として値渡しにしないと上手く機能しません。
 つづいて、FSO.GetFolder(myPath).SubFolders で、サブフォルダの一覧を得ます。For Each ではサブフォルダを1つづつ処理しますが、処理する関数は、自分自身と同じ searchAllFileForFSO関数になります。

 自分自身に入れてしまって処理がぶつからないのか?と思われる方もいるかもしれませんが、実際には関数もメモリ上に展開されるので、2回呼び出されると2つのコピーがメモリ上にあってそれぞれ処理を行うことになります。

 2回目に呼び出されたsearchAllFileForFSO関数は、サブフォルダを引数として受けているので、サブフォルダ内のファイル一覧を取得しに行きます。
 その中にサブフォルダがあれば、さらに3回目の呼び出してメモリ上に展開されることになります。

 このようにしてどんなにフォルダが深くても次々とフォルダを辿って行くことになります。もちろん深ければ深いほどメモリ上に展開されるためメモリは圧迫さることになります。

再帰呼び出しの注意点

 再帰呼び出しで注意すべき点を見ていきましょう。
今回は、Debug.Printでファイル名を取得してきましたが、シートに書き込んでいく処理を考えてみましょう。

 シートに書き込む際には、何行目に書き込めば良いかをセットしなければなりませんのでカウント用の変数が必要になってきます。前回のプログラムにカウント用の変数を使うにはどうしたら良いでしょうか?

 再帰呼び出しするsearchAllFileForFSO関数内に、カウンタ変数を定義するわけにはいきません。この場合は、グローバル変数を用意するか、引数にカウンタ変数を用意することになります。グローバル変数にすると、どこか別の処理で値が書き換えられてしまうかもしれないので、引数で渡す方法にしたいと思います。
 引数に関しては、パスを指す myPath ByVal (値渡し) 。カウンタ変数 i ByRef (参照渡し) にします。

Sub searchAllFileForFSO2(ByVal myPath As String, ByRef i As Long)
    Dim FSO As Object
    Dim myFile As Object
    Dim myFolder As Object
    
    Set FSO = CreateObject("Scripting.FileSystemObject")
    
    For Each myFile In FSO.GetFolder(myPath).Files
        Cells(i, "A") = i
        Cells(i, "B") = myFile
        i = i + 1
    Next myFile
    
    'サブフォルダを探す(再帰処理)
    For Each myFolder In FSO.GetFolder(myPath).SubFolders
        Call searchAllFileForFSO2(myFolder, i) 'ここで自分自身を呼び出す
    Next myFolder
    
    Set FSO = Nothing
End Sub

'呼び出す関数
Sub Macro2()
    Dim myPath As String
    Dim i As Long
    myPath = "C:\tools"

    i = 1
    Call searchAllFileForFSO2(myPath, i)
End Sub

 今回は、関数名に2を付けて名前を変更しています。関数名を変更するときは、再帰呼び出し関数の名前を変更することをお忘れなく。

 セルへの書き込みはCellsを使っていますが、行を1つずつ増やすためにカウンタ変数である i を追加しています。

 再帰呼び出しするsearchAllFileForFSO2関数の引数に i を追加し、Macro2関数の方で i を定義、初期化しています。searchAllFileForFSO2関数内で、セルに書き込みが行われた後で、i = i + 1 として次の行に移動していくようにしています。
 これでセルにファイル名を書き込む処理が出来上がりました。

コメント

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