Dir関数でサブフォルダを取得する


Dir関数は引数に指定したファイルが存在したとき、そのファイル名を返す関数です。引数にはワイルドカードを指定できます。たとえば、こんな感じです。C:\Workフォルダには「tanaka.xls」と「tanaka2.xls」が存在します。なお、カレントフォルダはC:\Workにしてあります。

Sub Sample()
    Dim buf As String, msg As String
    buf = Dir("tanaka*.*")
    Do While buf <> ""
        msg = msg & buf & vbCrLf
        buf = Dir()
    Loop
    MsgBox msg
End Sub

さて、Dir関数は第二引数に定数を指定することで「隠しファイル」や「読み取り専用」属性が設定されているなど、特定のファイルだけを対象にすることができます。Dir関数の第二引数に指定できる定数と意味は次の通りです。

定数 内容
vbNormal 0 標準ファイル
vbReadOnly 1 読み取り専用ファイル
vbHidden 2 隠しファイル
vbSystem 4 システムファイル(Macintoshでは使用できません)
vbVolume 8 ボリュームラベル(Macintoshでは使用できません)
vbDirectory 16 フォルダ
vbAlias 64 エイリアスファイル(Macintoshでのみ使用できます)

複数の属性を指定するときは、定数を合計します。たとえば「読み取り専用」の「隠し属性」ファイルを調べたいのなら、

Sub Sample2()
    Dim buf As String, msg As String
    buf = Dir("tanaka*.*", vbReadOnly + vbHidden)
    Do While buf <> ""
        msg = msg & buf & vbCrLf
        buf = Dir()
    Loop
    MsgBox msg
End Sub

とします。しかし、この引数は実に使いにくいです。たとえば今、フォルダに次のファイルが保存されていたとしましょう。

  • Book1.xls … 標準ファイル
  • Book2.xls … 読み取り専用ファイル
  • Book3.xls … 隠しファイル

このとき、上のSample2を実行すると次のような結果になります。

vbReadOnly + vbHiddenですから、Dir関数は「読み取り専用」と「隠し属性」のファイルだけを返すはずですが、実際にはどちらでもない標準ファイルのBook1.xlsまで返ってきています。これは、標準ファイルを示す定数vbNormalの実体が「0」という数値だからです。

【読み取り専用+隠しファイル】
vbReadOnly + vbHidden = 1 + 2 = 3

【標準ファイル+読み取り専用+隠しファイル】
vbNormal + vbReadOnly + vbHidden = 0 + 1 + 2 = 3

ね、どんな定数を指定しても、常に「標準ファイル」は指定されていることになるんです。

さて本稿のテーマは「Dir関数でサブフォルダを取得する」ですが、このときも、常に指定される「標準ファイル」が問題になります。ここでは、C:\Workに次のようなファイルとサブフォルダがあるとします。

このサブフォルダ「tanaka」と「tanaka2」を取得するために、次のようなコードではうまくいきません。理由は上記と同じです。

Sub Sample3()
    Dim buf As String, msg As String
    buf = Dir("*.*", vbDirectory)
    Do While buf <> ""
        msg = msg & buf & vbCrLf
        buf = Dir()
    Loop
    MsgBox msg
End Sub

第二引数のvbDirectoryは、vbNormal + vbDirectoryと同じ意味ですから、フォルダだけでなく標準ファイルも対象になってしまいました。

.
..
tanaka        <フォルダ>
tanaka.xls    標準ファイル
tanaka2       <フォルダ>
tanaka2.xls   標準ファイル

ところで見慣れないものがあります「.」と「..」とは何でしょう。MS-DOSの時代には必須の知識だったのですが、「.」は自分自身のフォルダを表し「..」は1つ上のフォルダを意味しています。カレントフォルダがC:\Workなら、

「.」 = C:\Work
「..」 = C:\

と同じことです。これらの相対フォルダは、階層構造のパスを指定するときに便利なのですが、それはそれとして。要するにここでは「.」と「..」もフォルダの一種とお考えください。だからvbDirectoryで取得されたのです。

さて、上のような返り値からサブフォルダだけを取得するにはどうしたらいいでしょう。まず考えつくのは「.」を含まないものがサブフォルダだということです。そこで次のようにしてみました。

Sub Sample4()
    Dim buf As String, msg As String
    buf = Dir("*.*", vbDirectory)
    Do While buf <> ""
        If InStr(buf, ".") = 0 Then msg = msg & buf & vbCrLf
        buf = Dir()
    Loop
    MsgBox msg
End Sub

これらは拡張子を含んでいませんのでサブフォルダ名です。

一般的には、こうして返り値に「.」を含むかどうかで判断できますが完璧ではありません。フォルダの名前には「.」を使うことができるのです。

サブフォルダ「tanaka2」の名前を「tanakaNo.2」としました。試すまでもありませんが、フォルダ名「tanakaNo.2」には「.」が含まれていますので、上記Sample4ではファイルと認識されてしまいます。

これを解決するには、結局ファイルやフォルダの名前が本当は何の名前なのかを見極めなければなりません。これには、ファイル名やフォルダ名の属性を調べます。「読み取り専用」のファイルには"読み取り専用属性"が設定されていますし、「システムファイル」には"システム属性"が設定されています。そして「フォルダ」には"フォルダ属性"が設定されています。したがって、Dir関数が返すファイル名/フォルダ名の属性が"フォルダ属性"だったら、それはサブフォルダだと確定できるわけです。

ファイル/フォルダの属性を調べるにはGetAttr関数を使います。GetAttr関数の返り値は、Dir関数の第二引数に指定できる定数と同じです。

【読み取り専用】
返り値:1 = vbReadOnly

【読み取り専用+隠しファイル】
返り値:3 = 1 + 2 = vbReadOnly + vbHidden

となります。返り値にどんな属性が含まれているかは And 演算子で調べます。

Sub Sample5()
    Dim buf As String, msg As String
    buf = Dir("Book*.xls", vbHidden + vbReadOnly + vbSystem)
    If GetAttr(buf) And vbHidden Then msg = msg & "隠し属性" & vbCrLf
    If GetAttr(buf) And vbReadOnly Then msg = msg & "読み取り専用属性" & vbCrLf
    If GetAttr(buf) And vbSystem Then msg = msg & "システム属性" & vbCrLf
    msg = buf & " は、" & vbCrLf & msg
    MsgBox msg
End Sub

以上のことから、Dir関数でサブフォルダ名だけを取得するには、次のようにすればいいとわかります。

  1. Dir関数の第二引数にvbDirectoryを指定する
  2. 返り値にvbDirectory属性が設定されているかをGetAttr関数で調べる
  3. 「.」と「..」は除く
Sub Sample6()
    Dim buf As String, msg As String
    buf = Dir("*.*", vbDirectory)
    Do While buf <> ""
        If GetAttr(buf) And vbDirectory Then
            If buf <> "." And buf <> ".." Then msg = msg & buf & vbCrLf
        End If
        buf = Dir()
    Loop
    MsgBox msg
End Sub