2011年12月25日日曜日

[ExcelVBA] 指定したフォルダ配下の全てのフォルダを調べる


■お題
指定したフォルダ以下にある全てのフォルダを表示すること

■解答例
ファイルシステムオブジェクトの「オブジェクト.SubFolders」を使えばよさそうですね。
まずは、考え方です。こういう風に考えてみました。
指定フォルダに対し、「オブジェクト.SubFolders」で指定フォルダ内のサブフォルダが取得できます。
例えば、指定フォルダには10個のフォルダがあったとします。
この情報を配列に格納しておきます。


次に、この配列に対しDoLoopで一つ一つ、サブフォルダを取得するのを繰り返せばよさそうです。
ただし、DoLoop内で新たに見つけたサブフォルダを処理の候補にいれる為共通の配列に格納する必要があります。

↑新たにみつけたものを追加。

そこで、引数の受け渡しに参照渡し(ByRef)で渡してフォルダのリストを更新し続けます。

まずは、サブフォルダの一覧を取得するテストコードを書いてみます。
  1. Option Explicit  
  2.   
  3. Sub test1()  
  4.     Dim FolderPath As String  
  5.     FolderPath = "C:\test"  
  6.       
  7.     Dim FSO As New FileSystemObject  
  8.     Dim Folders As Folders, Folder As Folder  
  9.     Set Folders = FSO.GetFolder(FolderPath).SubFolders  
  10.   
  11.     For Each Folder In Folders  
  12.         Debug.Print Folder.Path  
  13.     Next  
  14. End Sub  
4行目:指定するファルダ名を格納する変数
5行目:今回は、C:\testフォルダパスを指定
7行目:ファイルシステムオブジェクトを生成
8行目:Folders型とFolder型のオブジェクトの変数を宣言
9行目:指定したフォルダ内の全てのフォルダの情報を取得
11行目:For文で8行目で取得した全てのフォルダ情報を一つずつ渡していく
12行目:フォルダ情報のうち、パスのプロパティをデバック表示する
13行目:繰り返し。


これで、指定したフォルダ内にあるフォルダパスを取得できますね。
では、参照渡しで一つの配列にフォルダパスを格納するあたりを確認するコードを書いてみます。
  1. Option Explicit  
  2.   
  3. Sub test2_main()  
  4.     Dim FolderPath As String  
  5.     FolderPath = "C:\test"  
  6.       
  7.     Dim FolderList As Variant  
  8.     FolderList = Array("FolderPass_1""FolderPass_2""FolderPass_3")  
  9.     Call test2_sub(FolderPath, FolderList)  
  10.       
  11.     Dim Folder As Variant  
  12.     For Each Folder In FolderList  
  13.         Debug.Print Folder  
  14.     Next  
  15. End Sub  
  16.   
  17. Sub test2_sub(ByVal FolderPath As StringByRef FolderList As Variant)      
  18.     Dim FSO As New FileSystemObject  
  19.     Dim Folders As Folders, Folder As Folder  
  20.     Set Folders = FSO.GetFolder(FolderPath).SubFolders  
  21.       
  22.   Dim i As Integer  
  23.     i = UBound(FolderList) + 1  
  24.       
  25.     For Each Folder In Folders  
  26.         ReDim Preserve FolderList(i)  
  27.         FolderList(i) = Folder.Path  
  28.                   
  29.         i = i + 1  
  30.     Next  
  31. End Sub  

- test2_main -
4行目:指定するファルダ名を格納する変数
5行目:今回は、C:\testフォルダパスを指定
7行目:フォルダパスを一括管理する用の変数です。
参照渡しでサブ関数に渡してフォルダパス情報を受け取るのに使います。
8行目:フォルダパスが追加されるか確認するために、適当に何か配列に格納しておきます。
9行目:パスと、配列を渡し、"test2_b"で第一引数のパス配下にあるフォルダ情報を格納させる。
11行目:参照渡しで格納してもらった配列の中身をFor文で確認する為For文で使う変数を宣言
12行目:For文で配列の一覧を確認
13行目:デバック表示する
14行目:繰り返し

- test2_sub -
18行目:ファイルシステムオブジェクトを生成
19行目:Folders型とFolder型のオブジェクトの変数を宣言
20行目:指定したフォルダ内の全てのフォルダの情報を取得
22行目:動的に配列を増やす際に使う変数を宣言。要素数の管理に使用。
23行目:再宣言するために現在の配列の最後の要素数を調べる。
そして、再宣言するために1多くしとく。(注:後で問題発生します)
25行目:For文で19行目で取得した全てのフォルダ情報を一つずつ渡していく
26行目:配列の要素数を再定義をする。 Preserveキーワードをつけないと今まで格納した
データが消えてしまうので、Preserveキーワードをつけておく。
27行目:最後の要素にフォルダパスを格納する
29行目:配列の再定義に使う変数を1つインクリメントする
30行目:繰り返し

メモ:Uboundは、配列の一番最後の要素数を返す。

あとは、DoLoopで繰り返す部分を作れば完成ですね。

  1. Option Explicit  
  2.   
  3. Sub main()  
  4.     Dim FolderName As String, Result() As String  
  5.     FolderName = "C:\test"  
  6.     Result = GetAllFolderPath(FolderName)  
  7.   
  8.     Dim f As Variant  
  9.     For Each f In Result  
  10.         Debug.Print f  
  11.     Next  
  12. End Sub  
  13.   
  14. Function GetAllFolderPath(ByVal FolderName As String)  
  15.       
  16.     Dim FolderPathList() As String, LastIndex As Integer  
  17.     LastIndex = -1  
  18.     Call GetFolderPath(FolderName, FolderPathList, LastIndex)  
  19.     If LastIndex = -1 Then  
  20.         GetAllFolderPath = Array("")  
  21.         Exit Function  
  22.     End If  
  23.       
  24.     Dim i As Integer  
  25.     i = 0  
  26.       
  27.     Do  
  28.         Call GetFolderPath(FolderPathList(i), FolderPathList, LastIndex)  
  29.         i = i + 1  
  30.     Loop While i < LastIndex  
  31.   
  32.     GetAllFolderPath = FolderPathList  
  33. End Function  
  34.   
  35. Sub GetFolderPath(ByVal FolderName As StringByRef FolderPathList As VariantByRef LastIndex As Integer)  
  36.     Dim FSO As New FileSystemObject  
  37.     Dim Folders As Folders, Folder As Folder  
  38.     Set Folders = FSO.GetFolder(FolderName).SubFolders  
  39.       
  40.     For Each Folder In Folders  
  41.         LastIndex = LastIndex + 1  
  42.         If LastIndex = 0 Then  
  43.             ReDim FolderPathList(LastIndex)  
  44.         Else  
  45.             ReDim Preserve FolderPathList(LastIndex)  
  46.         End If  
  47.           
  48.         FolderPathList(LastIndex) = Folder.Path  
  49.     Next  
  50. End Sub  
動的配列用の変数は、要素数が確定する前にUBoundを使うとエラーとなるので 使わずに、要素数を知るために、管理用に一つ変数を増やして参照渡しして管理してます。 使い方は、GetAllFolderPathに調べたいフォルダのパスを引数として渡す。 で、要素数を指定してない配列を返り値に受け取る。 以上。 ちなみに、下記フォルダ構成だと。
イミディエイトウィンドウには、こんな出力になる。

0 件のコメント: