2012年12月20日木曜日

[ExcelVBA] 再帰処理 - サブフォルダ内のファイル


アドベントカレンダー 20日目

再帰処理といえば、ある一定の処理が繰り返し
樹木の枝のように発生する場合に使う処理ですね。

例えば、サブフォルダ内のファイルを検索する場合



該当するファイルを探すという行為をフォルダを見つけるたびに1階層下に移動し繰り返しているだけですよね。
こういったパターンの時に使えますね。
他の例だと、クイックソートとかも使いどころですよね。

まずは、再帰処理を使う必要がないくらい単純な例を使って実際に使ってみましょう。
では、10カウントダウンする関数を考えてみます。

普通に書くと、以下のように書けば実現できます。
Option Explicit

Sub SampleCode()
    Call CountDown(10)
End Sub

Sub CountDown(ByVal num As Integer)
    Dim i As Integer
    
    For i = num To 1 Step -1
        Debug.Print i
    Next
End Sub

これと同様のことを再帰処理で実現すると…
Option Explicit

Sub SampleCode()
    Call CountDown(10)
End Sub

Sub CountDown(ByVal num As Integer)
    Debug.Print num
    If 1 < num Then Call CountDown(num - 1)
End Sub
シンプルですねぇ。 これは、カウントするという行為を10回繰り返していて 値だけが変わっているということに着目して書き換えたものです。 ただ、fot文で簡単にかけちゃうといまいち再帰処理のメリットを感じませんね そんなわけで、実際にサブフォルダ内のファイルを探すプログラムあたりを 書いてみると再帰処理のよさを感じられると思うので実際に書いてみるとよいと思います。 参考までに、自分が作成した関数を掲載しておきます。
Option Explicit

Function GetSubFolder(ByRef FilePaths As Object, ByVal CurrentFolder As String, Optional ByVal NameRule As String = "*")
    Dim FileName As String
    Dim Folder   As Variant
    Dim Folders  As Object
    Dim FSO      As Object
    Dim TopFlag  As Boolean
    
    '** Objectの生成 **
    Set FSO = CreateObject("Scripting.FileSystemObject")
    
    '存在しないパスのときは、関数を抜ける
    If Not FSO.FolderExists(CurrentFolder) Then Exit Function
    
    '該当するファイルの取得
    FileName = Dir(CurrentFolder & "\" & NameRule)
    
    Do While FileName <> ""
        
        FilePaths.Add CurrentFolder & "\" & FileName, FileName
        FileName = Dir()
    
    Loop

    'サブフォルダの処理
    Set Folders = FSO.GetFolder(CurrentFolder)
    
    'サブフォルダを一つずつループで処理する
    For Each Folder In Folders.SubFolders
        
        'さらにその下の階層を調べる
        Call GetSubFolder(FilePaths, Folder.Path, NameRule)
    
    Next

    Set FSO = Nothing
    Set Folders = Nothing
End Function

'実際に使ってみる
Sub SampleCode()
    Dim CurrentFolder As String
    Dim FilePath      As Variant
    Dim FilePaths     As Object
    
    '** Objectを生成 **
    Set FilePaths = CreateObject("Scripting.Dictionary")
    
    '引数
    '第一引数:見つかったパスを格納するDictionary
    '          Dictionaryはキーにフルパス、値にファイル名
    '第二引数:検索開始のフォルダパス
    '第三引数:Dir関数同様に探しだすファイル名をアスタリスクを使って指定。
    
    'ファイルを探し出すフォルダパス
    CurrentFolder = "C:\Documents and Settings\Owner\My Documents\excel\test"
    
    '検索開始
    Call GetSubFolder(FilePaths, CurrentFolder, "*")

    '検索結果を出力
    For Each FilePath In FilePaths
        Debug.Print "フルパス:" & FilePath
'        Debug.Print "ファイル:" & FilePaths.Item(FilePath)
    Next
    
    Set FilePaths = Nothing
End Sub

0 件のコメント: