MkDirステートメントやFileSystemObjectのCreateFolderメソッドは、
作りたいフォルダの親フォルダがないとエラーになります。
例えば以下のコードは「商品フォルダしかない」状況では実行できません。
FSO.CreateFolder "C:\Users\○○\Desktop\商品\くだもの\柑橘\みかん"
この目的を果たすためには、
FSO.CreateFolder "C:\Users\○○\Desktop\商品\くだもの" FSO.CreateFolder "C:\Users\○○\Desktop\商品\くだもの\柑橘" FSO.CreateFolder "C:\Users\○○\Desktop\商品\くだもの\柑橘\みかん"
このように上から順次実行されるようコードを書く必要があります。
この解決策としてストレートで分かりやすいのは、
- フォルダパスを\でSplitして各階層ごとのフォルダ名に分ける
- 各フォルダ名を1つずつつなげて上から順にフォルダを作る
というコードかなと思います。
中核のコードだけ抜粋すると、
Arr階層ごとのフォルダ名 = Split(作成フォルダのフルパス, "\") For i = 1 To UBound(Arr階層ごとのフォルダ名) 作成フォルダパス = 作成フォルダパス & "\" & Arr階層ごとのフォルダ名(i) MkDir 作成フォルダパス Next
こんなコードです。
詳細についてはこちらをどうぞ。
さてこの処理を行うもう一つの方法として、
再帰関数を用いる方法があります。
それがこちら
Sub フォルダを作成する(作成フォルダパス As String) Dim 親フォルダパス As String 親フォルダパス = FSO.GetParentFolderName(作成フォルダパス) If FSO.FolderExists(親フォルダパス) = False Then Call フォルダを作成する(親フォルダパス) End If FSO.CreateFolder 作成フォルダパス End Sub
短くていいですね。
再帰関数にあまり触れたことがない方向けに説明しますと、
再帰関数とは「自分自身を呼ぶ関数」のことです。
上記の関数も自分で自分をCallしている部分がありますね。
再帰処理に慣れていないと処理の順番を追いづらいと思いますので、
実際に処理される様子をコードに展開してみます。
例えば以下のコードを実行してみましょう。
Call フォルダを作成する("C:\Users\○○\Desktop\商品\くだもの\柑橘\みかん")
「商品」フォルダが存在し、「くだもの」フォルダが存在しない場合は、
以下のようにコードが実行されることになります。
Sub フォルダを作成する("C:\Users\○○\Desktop\商品\くだもの\柑橘\みかん") 親フォルダパス = "C:\Users\○○\Desktop\商品\くだもの\柑橘" If FSO.FolderExists(親フォルダパス) = False Then ' ← True Sub フォルダを作成する("C:\Users\○○\Desktop\商品\くだもの\柑橘") 親フォルダパス = "C:\Users\○○\Desktop\商品\くだもの" If FSO.FolderExists(親フォルダパス) = False Then ' ← True Sub フォルダを作成する("C:\Users\○○\Desktop\商品\くだもの") 親フォルダパス = "C:\Users\○○\Desktop\商品" If FSO.FolderExists(親フォルダパス) = False Then ' ←ここでFalse FSO.CreateFolder "C:\Users\○○\Desktop\商品\くだもの" End Sub End If FSO.CreateFolder "C:\Users\○○\Desktop\商品\くだもの\柑橘" End Sub End If FSO.CreateFolder "C:\Users\○○\Desktop\商品\くだもの\柑橘\みかん" End Sub
- 親フォルダがない間は自分自身をCallし続けて深くなっていき、
- 親フォルダが見つかると一転、そこからフォルダを作成して戻ってくる。
というイメージが伝わりますでしょうか?
最後のフォルダ作成部分が、しっかりと
FSO.CreateFolder "C:\Users\○○\Desktop\商品\くだもの" FSO.CreateFolder "C:\Users\○○\Desktop\商品\くだもの\柑橘" FSO.CreateFolder "C:\Users\○○\Desktop\商品\くだもの\柑橘\みかん"
この形で実行されているのを見ると、よく出来てるな~って思いますよね。
こんな感じで「自分自身を呼ぶ関数」が再帰関数です。
今回の例のように、「どこまで処理を進めるか」が不定の時に、
その終点が見つかるまで自身を呼び続けていくような使い方が多いです。
探索系のプログラムに使われることが多いロジックでしょうか。
VBAではめったに見ることはありませんし、
再帰関数を知っていると使える場面に遭遇するかというと、
そうでもなさそうな気はします。
子フォルダも含めたフォルダ内のすべてのファイルを処理する
ときにたまに出てくるくらいでしょうかね。
ロジックとしてはとても面白いと思いますので、
興味があれば、他の再帰関数も調べてみてください。
おまけ:再帰関数の関数名
個人的にずっと悩んでいることなのですが、
再帰関数ってどういう風に名付ければいいんでしょうかね。
例えば今回の関数
Sub フォルダを作成する(作成フォルダパス As String) Dim 親フォルダパス As String 親フォルダパス = FSO.GetParentFolderName(作成フォルダパス) If FSO.FolderExists(親フォルダパス) = False Then Call フォルダを作成する(親フォルダパス) End If FSO.CreateFolder 作成フォルダパス End Sub
これって、再帰関数であることは関数名からはわからないじゃないですか。
よく「コメントなしでもロジックがわかるように」なんていわれますけど、
再帰処理ってどういう風に関数名に表現すればいいのかなって。
まあ、
Sub フォルダを作成する(作成フォルダパス As String) Dim 親フォルダパス As String 親フォルダパス = FSO.GetParentFolderName(作成フォルダパス) ' 親フォルダが存在する階層まで遡って再帰呼び出し If FSO.FolderExists(親フォルダパス) = False Then Call フォルダを作成する(親フォルダパス) End If FSO.CreateFolder 作成フォルダパス End Sub
このコメントで十分わかるんですけどね。
呼出元が「この関数は再帰関数だ」と認識する必要もないですし。
もし再帰ロジックを関数名で表現するなら、
ユーザーが呼ぶ関数と実際の再帰部分は別にして、
Sub フォルダを作成する(作成フォルダパス As String) If Dir(作成フォルダパス, vbDirectory) <> "" Then Exit Sub Call 親フォルダがなければ再帰呼出で親フォルダを作成してからフォルダを作成する(作成フォルダパス) End Sub Private Sub 親フォルダがなければ再帰呼出で親フォルダを作成してからフォルダを作成する(作成フォルダパス As String) Dim 親フォルダパス As String 親フォルダパス = FSO.GetParentFolderName(作成フォルダパス) If FSO.FolderExists(親フォルダパス) = False Then Call 親フォルダがなければ再帰呼出で親フォルダを作成してからフォルダを作成する(親フォルダパス) End If FSO.CreateFolder 作成フォルダパス End Sub
こんな感じになるんですかね。
わかりやすいけど長いですよね。
こんな感じで関数名をとにかく丁寧に名付け隊の私は、
再帰関数が出てくるたびにいっつも命名に悩みます。
出てくるたびにといっても、実務ではめったに扱うことはなく、
Twitterのプログラミングお題の回答とかがほとんどなため、
そんな関数名なんてFnでいいはずなんですけどね。
それでもやっぱり気になるものは気になるんですよ。
ということでみなさんも、再帰関数を使う際は関数名に悩んでみてください。