見出し画像

マクロのある暮らし(17回目) - ファイル一覧つくっちゃう?

こんにちは!なるーらぼです!
新学期もはじまって、息子の参観日にいってきました。
小学校の授業風景というかって、30年近く前とそれほど変わってないなぁと思うのはわたしだけでしょうか?教科書もさほど変わってないので、「変わらないことを学ぶ場」なんだなーと思ったりしました。

ファイル一覧

今回は以前にも少しだけ紹介したファイルシステムオブジェクトを使ったものをやってみようと思います。

「このフォルダの中からふかーいところまでフォルダをみていって、Excelファイルだけ探したいなー」というときがありますよね。そういうときは迷わずWindowsのエクスプローラから検索しましょう(キリッ

でも、その結果をファイルに残したりとかしたいよなーというときがあると思います。そういうときこそマクロですよね。

正直言いまして、マクロでなくても黒い画面が得意な方はサクッとやってしまうことができるので「そういう人たちにお願いしなくてもいいようにしたい!」という意欲的な方はお願いします!

ファイルシステムオブジェクト

以前にもつかったのですが、覚えていますか?参照設定して使います。

これですね、これを参照しましょう。

では例えばドキュメントフォルダ内にあるファイルの一覧をつくろうと思ったらどうしましょう?そういうときはエクスプローラでの操作を思い出してみるといいでしょう。まずエクスプローラでファイルやフォルダをみたいフォルダまで行きますよね。そうしたらフォルダやファイルが名前などでばーっと表示されます。

これをマクロからするときにはFilesコレクションとSubFoldersコレクションを使うといいですね。

Function note_mu(path As String)
 Dim fso As New FileSystemObject
 Dim d As Folder
 Dim sd As Folder
 Dim f As File
 '' フォルダが指定されていなかったり
 '' 指定されたフォルダが存在しないときは終了する
 If Len(path) = 0 Then: Exit Function
 If Dir(path, vbDirectory) = "" Then: Exit Function
 '' フォルダを開くイメージ
 Set d = fso.GetFolder(path)
 '' フォルダ内のフォルダを列挙する
 For Each sd In d.SubFolders
  Debug.Print sd.Name
 Next
 '' フォルダ内のファイルを列挙する
 For Each f In d.Files
  '' 名前、ファイル種類、最終更新日時
  Debug.Print f.Name & " : [" & f.Type & "] -> " & f.DateLastModified
 Next
 Set f = Nothing
 Set d = Nothing
 Set fso = Nothing
End Function

こんな感じで実行すると、指定したフォルダ内にあるフォルダ名とファイルの一覧がイミディエイトウィンドウへ出力されます。

note_mu("C:\Users\hoge\Pictures")

もしもフォルダの中にあるフォルダの中も、さらにその中にあるフォルダの中も…とやっていくのでしたらSubFoldersを複数回使用すればいいことがわかります。しかし、いくつあるかわからないとしたら…

ちょっとやっかいですね。

自分自身を呼び出す

ちょっとやっかいなことが分かったのですが、実は解決方法は簡単です。自分自身を呼び出せばいいのです。

「ちょっと何言ってるかわからない…」と思うかもしれませんが、関数などのサブルーチンが自分自身を呼び出してはいけないということにはなっていません。ですから、することはできます。

この方法は注意が必要で、どこかで終了しなければWhileやForといった反復構文を使うことなく無限ループに陥ることがあります。また、メモリを使い果たしてしまうということも考えられますので用法用量を守って正しくお使いください。

ということでコードをほんの少しだけ書き直しました。ご覧ください。

Function note_mu(path As String)
 Dim fso As New FileSystemObject
 Dim d As Folder
 Dim sd As Folder
 Dim f As File
 If Len(path) = 0 Then: Exit Function
 If Dir(path, vbDirectory) = "" Then: Exit Function
 Set d = fso.GetFolder(path)
 For Each sd In d.SubFolders
  Debug.Print " -- " & sd.Name
  note_mu sd.Path
 Next
 For Each f In d.Files
  Debug.Print f.Name & " : [" & f.Type & "] -> " & f.DateLastModified
 Next
 Set f = Nothing
 Set d = Nothing
 Set fso = Nothing
End Function

コメント部分は取り除いてしまいましたが、どこが変わったかお分かりでしょうか?

そうです、SubFoldersを使ったFor Eachループのところですね。

 For Each sd In d.SubFolders
  Debug.Print " -- " & sd.Name
  note_mu sd.Path
 Next

ここでは「note_mu sd.Path」としてフォルダ内のフォルダパスを渡して自分自身を呼び出しています。そうすると、同じ処理が別のフォルダを起点にして実行されます。さらにこの中にもフォルダがあるとしたら、そのフォルダを起点にして再度実行されます。
最終的にそれ以上フォルダが見つからなくなったらそのフォルダ内のファイルを出力して終了します。すると、その上の階層のフォルダでも…というようになって結果的にフォルダ内のふかーーーいところまでファイルの一覧ができあがります。

上記の話の中で、「それ以上フォルダが見つからなくなったら」というのが終了条件です。これがあるので無限ループには陥ることなく復帰してきます。

活用方法

上記のコードは活用することができるでしょう。例えば、2つ目の引数にワイルドカードのパターンを受け取ることができるようにすると、Like演算子で該当するファイル名だけを出力することができるようになると思いますよね。

さらに、あるファイルサイズ以上のもの、あるいは以下のものといったフィルタをすることもできるでしょうし、あるいは日付で期間や日付を指定できるようにすれば、「4月に更新されたファイル」とか「作成されて1か月以上経過したファイル」といったようなこともできるようになります。

結果は出力するだけでなく、例えば配列などへ格納するようにすればCSVにも加工できるでしょうしHTML風にすればレポートにもできます。

もちろん、Excelのワークシートに出力しても構いませんよね。

最後に

今回はファイル一覧をつくってみました。ファイル名をつけるルールなどを決めていればファイルのフォルダ分けなども自動で行うことができるようになります。そういうの、お仕事上で結構ありませんか?

無理にする必要はありませんが、デジカメやスマホで撮影した画像をパソコンに溜めてしまっている方はそういった整理にもいいかもしれませんよ。

ではまた、良い一日を!

この記事が気に入ったらサポートをしてみませんか?