Dir関数の使い方。ファイル名やフォルダ名の取得方法。(Excel VBA)

【本日のミッション】

Excel VBAにて使用するDir関数。ファイルやフォルダを取得する仕組みを検証せよ。

ミッションの概要

Dir関数って難しくないですか?

ファイル取得するだけの時までは「親切な関数だなぁ~(#^.^#)」って思っていたのですが、フォルダ検索しようとすると、とたんに難しくなって・・・。

今回はそんなDir関数のややこしさに迫ってみたいと思います。(←どんなミッションやねん)

Dir関数とは

Dir関数は、第1引数に指定したファイル・フォルダが存在する場合、パスを除いたファイル名・フォルダ名を返します。存在しない場合は長さ0の文字列 ”” (空欄)を返します。

Dir(ファイル名・フォルダ名を表す文字列,ファイル・フォルダの属性)

Dir (“C:\VBA\Dir関数検証\02_標準ファイル.xlsx”)  の場合

  • ファイルが存在する場合(ファイル名を返す)→  02_標準ファイル.xlsx
  • ファイルが存在しない場合(空欄を返す)      →  ””

第1引数にはワイルドカードを使用することができるので、取得したいファイル名・フォルダ名の幅を広げることができます。後述の「第1引数の指定拡張子が3文字になる場合の注意点」もご覧ください。

Dir(”C:\VBA\Dir関数検証\*.*“)

参考:ワイルドカードとは。使い方いろいろ。(Excel)

第2引数に下記定数(値)を指定することで、特定の属性のオブジェクトのみを取得の対象とすることができます。

定数属性
vbNormal (既定)0標準ファイル
vbReadOnly1読み取り専用ファイル
vbHidden2隠しファイル
vbSystem4システム ファイル
vbVolume8ボリューム ラベル。この値を指定すると、すべての属性は無効になります。
vbDirectory16フォルダ
vbAlias64エイリアスファイル

※4、8はMacでは使えません。また、64はMacintoshのみ使えます。

複数の属性を取得対象としたい場合は「+」にて合算することができます。

vbReadOnly + vbHidden」とした場合は、読み取り専用ファイルと隠しファイルの両方を取得することができます。

fn = Dir(pt & "\*.*", vbReadOnly + vbHidden)

Dir関数は一度引数を指定すると、次に別の引数を指定するまで、同じ引数に対する処理を行います。

fn=Dir("C:\VBA\Dir関数検証\*.*")
fn=Dir()
fn=Dir()

1行目の処理では、変数fnに「C:\VBA\Dir関数検証\*.*」に一致するファイルが存在すれば、そのファイル名を返します。

2行目以降の「fn=Dir()」では引数を指定していませんが、「C:\VBA\Dir関数検証\*.*」に一致する別のファイル名を返します。

その処理を繰り返し、「C:\VBA\Dir関数検証\*.*」に一致するファイルが無くなった時、長さ0の文字列 ”” 空欄 を返します。

何度も「fn=Dir()」を書くのは面倒だし、どのタイミングでファイルが無くなるかわからないので、Do While ~ Loopを使って繰り返し処理を行います。

fn=Dir("C:\VBA\Dir関数検証\*.*")
Do While fn <> ""         'fnが空欄になるまでDo While内の処理を続ける 
    ~fnで行いたい処理を記述~
    fn = Dir()            '変数fnに次のファイル名を格納(ファイルが無い場合は空欄になる) 
Loop

Dir関数 仕様検証用ファイルの準備

Dir関数は、ファイル名やフォルダ名を取り出すことができるのですが、ちょっとややこしいクセがあります。

そのクセを検証するために、下記フォルダにいろいろな属性のファイルを準備しました。

属性をファイル名にしています。

それぞれの属性を指定することで、どんなファイル名・フォルダ名を取得することができるのか見ていきたいと思います。

検証用のファイルの準備です。

Excelマクロ有効ブック(.xlsm)のファイルを新規作成します。

そのファイルのシートに下記の通り、フォルダのパスを入力するセル(A3)、取得したファイル名・フォルダ名を書き出すセル(6行目1列目)を準備します。

フォルダパスには、ファイル名・フォルダ名を取得したいフォルダのフルパスを入力します。

■■■スポンサーリンク■■■

標準ファイル名を取得したい場合

先ほど作った検証用ファイルに、下記プロシージャを追加します。

Sub 指定したフォルダ内の標準ファイル名を全て取得()

    '【変数】
    Dim fn As String   'ファイル名
    Dim t_row As Long  '処理ターゲット行
    Dim pt As String   '処理対象パス

    '■変数セット
    pt = Range("A3")   '変数ptにセルA3のパスをセット
    t_row = 6          '変数t_rowにを書き出す初めのセルの行番号をセット

    fn = Dir(pt & "\*.*") '変数fnに1個目のファイル名を格納

    Do While fn <> ""          'fnが空欄になるまでDo While内の処理を続ける
        Cells(t_row, 1) = fn   'Cells(t_row, 1)にファイル名を書き出し
        t_row = t_row + 1      'ファイル名を書き出すセルの行番号+1(一つ下の行番号)
        fn = Dir()             '変数fnに次のファイル名を格納(ファイルが無い場合は空欄になる)
    Loop

End Sub

標準ファイル名を取り出したい時は、標準ファイルが規定値のため、第2引数は省略することができます。

定数属性
vbNormal (既定)0標準ファイル

第1引数のファイルパスだけ指定しました。

fn = Dir(pt & "\*.*")

結果はこちらです。

予想していた結果と異なりました・・・。
標準ファイルだけではなく、読み取り専用ファイルも取得しています・・・。
隠しファイルは除外されているのですが・・・。

この理由はわからないのですが、通常業務で標準ファイルだけ必要で読み取り専用ファイルは除外、という場面はあまりないかと思います。むしろ読み取り専用にしていたばかりに処理から除外されてしまう方が怖いと思います。

読み取り専用ファイルを取得したい場合

読み取り専用ファイルのみを取り出したい時は、下記プロシージャを追加します。

Sub 指定したフォルダ内の読み取り専用ファイル名を全て取得()

    '【変数】
    Dim fn As String   'ファイル名
    Dim t_row As Long  '処理ターゲット行
    Dim pt As String   '処理対象パス

    '■変数セット
    pt = Range("A3")   '変数ptにセルA3のパスをセット
    t_row = 6          '変数t_rowにを書き出す初めのセルの行番号をセット

   fn = Dir(pt & "\*.*", vbReadOnly) '変数fnに1個目のファイル名を格納

    Do While fn <> ""          'fnが空欄になるまでDo While内の処理を続ける
        If GetAttr(pt & "\" & fn) And vbReadOnly Then '標準ファイルを取得しないために必要
            Cells(t_row, 1) = fn   'Cells(t_row, 1)にファイル名を書き出し
            t_row = t_row + 1      'ファイル名を書き出すセルの行番号+1(一つ下の行番号)
        End If
        fn = Dir()             '変数fnに次のファイル名を格納(ファイルが無い場合は空欄になる)
    Loop

End Sub

第1引数にファイル名パス、第2引数に読み取り専用ファイル属性の定数「vbReadOnly」を指定します。

定数属性
vbReadOnly1読み取り専用ファイル
fn = Dir(pt & "\*.*", vbReadOnly)

この処理だけの場合、結果は標準ファイルも取得してしまいます。

標準ファイルを取得しないために、下記処理を追加します。

If GetAttr(pt & "\" & fn) And vbReadOnly Then
End if

この処理を追加すると、読み取り専用ファイルのみ取得することができます。

なぜこんな結果になってしまうのか。GetAttr関数を使わないといけない理由につきましては、後述の「指定した属性以外の「標準ファイル」が含まれる理由」「GetAttrとは」の項目でお話しさせていただきます。

隠しファイルを取得したい場合

隠しファイルのみを取得したい場合は、上記「読み取り専用ファイルを取得したい場合」のコード「vbReadOnly」となっている2ヶ所を「vbHidden」と変更して実行します。

定数属性
vbHidden2隠しファイル
fn = Dir(pt & "\*.*",vbHidden)
If GetAttr(pt & "\" & fn) And vbHidden Then
End if

結果は下記の通りです。隠しファイルの属性のあるものが取得されました。

GetAttr関数を使わないといけない理由につきましては、後述の「指定した属性以外の「標準ファイル」が含まれる理由」「GetAttrとは」の項目でお話しさせていただきます。

読み取り専用・隠しファイル両方を取得したい場合

隠しファイルを取得したい場合は、上記「読み取り専用ファイルを取得したい場合」のコード「vbReadOnly」となっている2ヶ所を「vbReadOnly+vbHidden」と変更して実行します。

定数属性
vbReadOnly1読み取り専用ファイル
vbHidden2隠しファイル

結果は下記の通りです。読み取り専用ファイルも隠しファイルも両方取得しています。

GetAttr関数を使わないといけない理由につきましては、後述の「指定した属性以外の「標準ファイル」が含まれる理由」「GetAttrとは」の項目でお話しさせていただきます。

■■■スポンサーリンク■■■

フォルダ名のみを取得したい場合

フォルダ名のみを取り出したい時は、下記プロシージャを追加します。

Sub 指定したフォルダ内のフォルダ名を全て取得()

    '【変数】
    Dim fd As String    'フォルダ名
    Dim t_row As Long   '処理ターゲット行
    Dim pt As String    '処理対象パス

    '■変数セット
    pt = Range("A3")         '変数ptにセルA3のパスをセット
    t_row = 6                '変数t_rowにフォルダ名を書き出す初めのセルの行番号をセット

    fd = Dir(pt & "\", vbDirectory)   '変数fdに1個目のフォルダ名を格納

    Do While fd <> ""                                  'fdが空になるまでDo While内の処理を続ける
        If GetAttr(pt & "\" & fd) And vbDirectory Then '標準ファイルを取得しないために必要
            If fd <> "." And fd <> ".." Then           '「.」と「..」を取得しないために必要
                Cells(t_row, 1) = fd                   'Cells(t_row, 1)にフォルダ名を書き出し
                t_row = t_row + 1                      'フォルダ名を書き出すセルの行番号+1(一つ下の行番号)
            End If
        End If
        fd = Dir()     '変数fdに次のフォルダ名を格納(フォルダが無い場合は空欄になる)
    Loop

End Sub

第1引数にフォルダパス、第2引数にフォルダ属性の定数「vbDirectory」を指定します。

定数属性
vbDirectory16フォルダ
fd = Dir(pt & "\", vbDirectory)

この処理だけの場合、結果は標準ファイル(読み取り専用ファイル)や「.」とか「..」とか、ナゾの記号まで取得しています。

標準ファイル名を取得したい場合」の項目でお話しした通り、標準ファイルを指定しているのに、読み取り専用ファイルも取得されてしまうようなので、今回も読み取り専用ファイルが含まれています。

Dir関数って、第2引数で指定した属性のオブジェクトしか対象にならないんじゃなかったっけ?

これらの標準ファイル(読み取り専用ファイル)を除外するために、GetAttr関数を使用します。詳細につきましては、後述の「指定した属性以外の「標準ファイル」が含まれる理由」「GetAttrとは」の項目をご覧ください。

If GetAttr(pt & "\" & fd) And vbDirectory Then
End if

また、ナゾの「.」や「..」も処理の対象外としたいので、下記条件にて取り除きます。
「.」「..」につきましては次の「「.」と「..」の正体」の項目をご覧ください。

If fd <> "." And fd <> ".." Then
End if

「.」と「..」の正体

「.」とは自分自身のいるカレントフォルダ、「..」はカレントフォルダの一つ上の階層のフォルダのことを指しています。

今回の処理では

fd = Dir(pt & "\", vbDirectory)

「pt=C:\VBA\Dir関数検証」としているので

「.」は ・・・「C:\VBA\Dir関数検証」自分自身のフォルダ

「..」は・・・「C:\VBA」                 一つ上の階層のフォルダ

となります。

今回の処理では「.」や「..」フォルダは必要ありませんので、

If fd <> "." And fd <> ".." Then
End if

という処理で、除外しています。

指定した属性以外の「標準ファイル」が含まれる理由

Dir関数は第2引数に指定した定数の値の合計で、どの属性を取得するか判断しています。

Dir関数の定数一覧を見てみましょう。オレンジ文字のところが定数の値です。
それぞれの定数には、決められた値があります。

定数属性
vbNormal (既定)0標準ファイル
vbReadOnly1読み取り専用ファイル
vbHidden2隠しファイル
vbSystem4システム ファイル
vbVolume8ボリューム ラベル。この値を指定すると、すべての属性は無効になります。
vbDirectory16フォルダ
vbAlias64エイリアスファイル

読み取り専用ファイル(vbReadOnly)と隠しファイル(vbHidden)を取得するときは下記のように「+」を使って合算します。

fn = Dir(pt & "\*.*", vbReadOnly + vbHidden)

Dir関数は第2引数の合計値「3」を受け取り、読み取り専用ファイルの「1」と隠しファイルの「2」と判断し、処理しています。

3 = vbReadOnly(1)+ vbHidden(2

実はこの時、もう一つの属性も含めて処理されています。

3 vbNormal0)+ vbReadOnly(1)+ vbHidden(2

それが標準ファイル vbNormal 0)です。

フォルダを取得する場合は「vbDirectory(16)」を指定します。

Dir関数はこの場合も

16 vbNormal0)+vbDirectory(16

標準ファイル vbNormal の「0」も含めて処理します。

どうあがいてもDir関数では、値が「0」の標準ファイルを処理対象としてしまいます。

指定した属性以外の標準ファイル(読み取り専用ファイル)を含めないようにするためにGetAttr関数を利用します。

GetAttr関数とAnd演算子

GetAttr関数はファイルまたはフォルダの属性を、整数 (Integer型) で返します。

If GetAttr(pt & "\" & fd) And vbDirectory Then
End if
定数説明
vbNormal(規定値)0標準ファイル
vbReadOnly1読み取り専用ファイル
vbHidden2隠しファイル
vbSystem4システム ファイル
vbVolume8ボリューム ラベル
vbDirectory16フォルダ
vbArchive32アーカイブ
vbAlias64エイリアス ファイル

※4、8、32はMacでは使えません。また、64はMacintoshのみ使えます。

GetAttr(pt & "\" & fd)

この場合、引数に指定した文字列がvbDirectory フォルダなら「16」を返します。

しかし、このフォルダに隠しファイルの属性がセットされていたら「18」を返します。

vbHidden 隠しファイル(2)+vbDirectory フォルダ(16)=18

でも、「18」ではどの属性かわかりません。

それを解決してくれるのが「And演算子」なのです。

ここで使われるAnd演算子は私たちがよく知っている

A And B  →   A かつ B

の意味では使われていません。

A And B

この場合のAnd演算子は、左辺のAと右辺のBをそれぞれビット単位に分解し比較します。

Aが「18」という値だった場合、

vbHidden 隠しファイル(2)+vbDirectory フォルダ(16

という風に、属性の値の単位に分解します。

左辺Aと右辺Bに同じビットが存在するときに「True」を返します。

GetAttr(vbHidden 隠しファイル(2vbDirectory フォルダ(16) And vbDirectory フォルダ(16

という場合は、And演算子

  • 左辺の  vbDirectoryフォルダ(16 
  • 右辺の  vbDirectoryフォルダ(16

が同じビットなので、「True」を返します。

第1引数の指定拡張子が3文字になる場合の注意点

Dir関数では、3文字の拡張子を指定する時に注意しないといけない点があります。

Dir(”C:\VBA\Dir関数検証\*.xls")

とした場合、「*.xls」だけではなく「*.xlsx」「*.xlsm」の拡張しファイルも取得されてしまいます。

Dir関数は3文字の拡張子を指定した時、「3文字の拡張子から始まる拡張子」として処理してしまいます。

↓Dir関数は、勝手にこういう拡張子指定だと判断します。

Dir(”C:\VBA\Dir関数検証\*.xls*")

「.xls」のみを取得したい場合は、下記の条件式で除外しましょう。

Do While fn <> ""        
    If LCase(fn) Like "*.xls" Then
        Cells(t_row, 1) = fn   
        t_row = t_row + 1      
    End If
    fn = Dir()    
Loop

【参考】

指定したフォルダ内のファイル名全てを取得(Excel VBA)
変数でよく使われる「buf」「tmp」の意味
Dir関数が取得するファイルの順番
指定したフォルダ内のフォルダ名全てをGetAttrを使って「エラー53 ファイルが見つかりません。」を出さずに取得(Excel VBA)
GetAttrとは?「= vbDirectory」ではなく「And vbDirectory」となるビット演算の疑問
フォルダ名だけを取得したい時に出てくる 「.」 と 「..」 とは?
指定したフォルダ内とサブフォルダ内全てのファイル名を取得(Excel VBA)
CreateObject(“Scripting.FileSystemObject”) を使ってサブフォルダを取得
再帰処理とは?フォルダ内とサブフォルダ内全てのファイル名を取得(Excel VBA)
指定したフォルダ内のサブフォルダのフォルダ名を全部取得(Excel VBA)
再帰処理とは?フォルダ内のサブフォルダのフォルダ名を全部取得(Excel VBA)
参照渡し「ByRef」と値渡し「ByVal」の違い(Excel VBA)
ファイルのフルパスからファイル名のみを取得 InStrRev関数(Excel VBA)
指定したフォルダの全ての階層のフォルダ名・サブフォルダ名・ファイル名を取得(Excel VBA)
FileSystemObjectとは?CreateObject 関数 FolderExists・GetFolderの使い方
FileSystemObject CreateObject関数を使う方法・ 参照設定を使う方法 違いを理解してエラー防止
再帰処理とは?指定したフォルダの全ての階層のフォルダ名・サブフォルダ名・ファイル名を取得(Excel VBA)
ファイルのフルパスからファイル名のみを取得 Split関数(Excel VBA)
Callステートメントとは 引数 括弧()の使い方(Excel VBA)
指定したフォルダ内から「特定の文字を含まないファイル名」を取得(Excel VBA)
ワイルドカードとは。使い方いろいろ。(Excel)
Dir関数の使い方。ファイル名やフォルダ名の取得方法。(Excel VBA)
指定したフォルダのファイル名を取得し、そのファイル名を一括で変換(Excel VBA)
Excel起動時に「コンパイルエラー」。64ビット システムで Declareステートメントに、PtrSafe属性を設定(Excel VBA )
「ファイルを開く」ダイアログボックス から ファイル名を取得(Excel VBA)
「ファイルを開く」ダイアログボックス から 複数 ファイル名を取得(Excel VBA)
指定フォルダ内のサブフォルダ全てをフォルダ構成のみ(空フォルダ)を別フォルダにコピー(Excel VBA)
再帰処理とは?指定フォルダ内のサブフォルダ全てをフォルダ構成のみ(空フォルダ)を別フォルダにコピー(Excel VBA)

■■■スポンサーリンク■■■