※ 冗談の通じない方はご退場願います。
はじめに私の立場を明確にしておくと、私は個人的にFileSystemObject(以降FSOと記載)が好きで、Dir関数はあまり好きではない。従ってDir関数で事足りる処理でも基本的にVBAコードではFSOを利用する。この記事ではその理由を述べ、Dir派の方をFSO派に改宗させることを目的としたいと思う。悪を滅し、正義の光あらんことを。なんつって。
さて、まずはDir関数がいかに貧相であるかをご覧いただきたい。Dirで出来ることは主に以下の三つである。
- 指定したファイル・フォルダの存在チェック
- 指定したパターンにマッチするファイル名の取得
- 指定したフォルダ内のサブフォルダ・ファイルの一覧取得
機能豊富なFSOにとってこれしきの事は朝飯前である。
FSOが如何に機能豊富であるかはFSOが備える以下のメソッド群を見てもらえば一目瞭然。
- BuildPath メソッド
- CopyFile メソッド
- CopyFolder メソッド
- CreateFolder メソッド
- CreateTextFile メソッド
- DeleteFile メソッド
- DeleteFolder メソッド
- DriveExists メソッド
- FileExists メソッド
- FolderExists メソッド
- GetAbsolutePathName メソッド
- GetBaseName メソッド
- GetDrive メソッド
- GetDriveName メソッド
- GetExtensionName メソッド
- GetFile メソッド
- GetFileName メソッド
- GetFolder メソッド
- GetParentFolderName メソッド
- GetSpecialFolder メソッド
- GetTempName メソッド
- MoveFile メソッド
- MoveFolder メソッド
- OpenTextFile メソッド
とはいえVBAの組み込み関数であるDirは特に設定なしで使えるのでこの点FSOは分が悪い。FSOを使用するには原則、参照設定という準備が必要なのだ。参照設定なしで使うとインテリセンスが効かずコーディング効率が著しく低下する。
まぁそもそもDirは単体ですべてのファイル操作を担っているわけではなく、同じFileSystemモジュールに以下の兄弟関数がいる。機能数という意味で比較対象にするなら以下も含めてやらないと不公平というものだろう。
- ChDrive 関数
- CurDir 関数
- CurDir$ 関数
- Dir 関数
- EOF 関数
- FileAttr 関数
- FileCopy 関数
- FileDateTime 関数
- FileLen 関数
- FreeFile 関数
- GetAttr 関数
- Kill 関数
- Loc 関数
- LOF 関数
- MkDir 関数
- Reset 関数
- RmDir 関数
- Seek 関数
- SetAttr 関数
しかし、ここで一つ注目していただきたい。FSOのメソッド名は英語としてそのまま意味が通じるのに対し、関数群の方は略記が多く、意味と名前の対応付けを、いちいち覚えておかないといけないのだ。他人がコードを読むときも、関数群の読みやすさは読み手の知識に左右される。対してFSOは簡単な英単語さえ知っていれば、おおよそ何をしているのか想像がつく。処理と名前が綺麗に結びついているからだ。
次にDir関数単体の機能と、FSOでそれに該当する機能を比べてみよう。
指定したファイル・フォルダの存在チェック
Dir関数では
次のように記載する。
Dir([ファイルパス])
Dir([フォルダパス])
ファイルやフォルダが存在した場合はファイル名やフォルダ名を返し、存在しない場合は空文字「""」を返す。このようなルールを、コードを書く人・読む人両方が覚えておかなければならない。
FSOでは
次のように記載する。
fso.FileExists([ファイルパス])
fso.FolderExists([フォルダパス])
ファイルやフォルダが存在した場合はTrueを返し、存在しない場合はFalseを返す。単純明快である。
比較結果
あるか・ないかを調べたいのだから、欲しい返事は「ある/ない」の二択だ。
VBAに置ける二択といえばブーリアン型。「True/False」で返してくれるFSOの方がシンプルで分かりやすい。
そして、プログラムを読む際も「単一パスの存在チェックをしていることが明示的」であるFSOに分がある。
指定したパターンにマッチするファイル名の取得
Dir関数では
次のように記載する。
Dir([パターン])
ここで[パターン]は「*」や「?」などのワイルドカードを含むパス名だ。これもルールを人が覚える必要がある。
また、この記述で返ってくるファイル名は1つだけだ。
2番目にマッチしたファイル名を返すには、もう一度Dirを引数無しで呼び出す必要がある。
そしてDirはすべてのマッチしたファイル名を返し終わると最後に空文字を返す。
このような煩雑なルールを覚えなければならないのだ。
FSOでは
FSOでは一括パターンマッチは出来ない。したがって、まずパス配下のファイルの一覧を取得することになる。
fso.GetFolder([フォルダパス]).Files
GetFolderにより取得したフォルダーオブジェクトのFilesメソッドはFilesというコレクションを返すので、Dirのように何度も呼び出す必要はない。
取得したら、For EachなどでそれぞれIf文による判定を行い、合致するパターンのファイルを個別により分けていく。
比較結果
ここでもDirは知識を要するのに対し、FSOは明示的で分かりやすいコードになる。
指定したフォルダ内のサブフォルダ・ファイルの一覧取得
Dir関数では
次のように記載する。
Dir([フォルダパス\*])
Dir([フォルダパス\*], vbDirectory)
ただし、属性にvbDirectoryを指定しても、ファイルも含めて返してくる。
これはvbNormalの実値が0で、vbDirectory(16) + vbNormal(0)と同じ判定結果になるためだ。
実際にフォルダかどうかを調べるにはGetAttr関数で返ってきたパスがフォルダーかどうか検証しなければならない。
なお、こちらも一度に返せるファイル名・フォルダ名はひとつなのでDirを引数無しで何度も呼び出す必要がある。
FSOでは
FSOでは次のように直感的に意味の分かる書き方ができる。
fso.GetFolder([フォルダパス]).Files
fso.GetFolder([フォルダパス]).SubFolders
それぞれの戻り値もFilesコレクション・Foldersコレクションで、For Eachループでスマートに処理できる。
まとめ
リーダブルコードを心がけるうえで、Dirはもはや害悪でしかない。今こそDir関数に正義の鉄槌を!FSO万歳。
注意)個人の感想です。随所に偏見が見られますので鵜呑みにしないようにお気をつけください。