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

【本日のミッション】

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

ミッションの概要

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

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

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

Dir関数とは

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

Dir(ファイル・フォルダのパス,ファイル・フォルダの属性)

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

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

第1引数にはワイルドカードを使用することができるので、取得したいファイル名・フォルダ名の幅を広げることができます。

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

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

引数の指定には下記注意事項があります。
このページの最後でご説明していますのでご覧ください。

  • 第1引数の指定拡張子が3文字になる場合の注意点
  • 引数のファイル(フォルダ)のパスが256バイトを超えるとエラーになる
  • 第1引数に空文字””を指定した場合の注意点

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

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

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

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

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

fn = Dir("C:\VBA\Dir関数検証\*.*", 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) = 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)

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

標準ファイルを取得しないために、下記GetAttr関数・And演算子処理で除外します。

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

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

なぜこんな結果になってしまうのか。GetAttr関数を使わないといけない理由につきましては、後述の

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

の項目でお話しさせていただきます。

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

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

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

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

標準ファイル・読み取り専用ファイルを取得しないために、下記GetAttr関数・And演算子処理で除外します。

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

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

なぜこんな結果になってしまうのか。GetAttr関数を使わないといけない理由につきましては、後述の

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

の項目でお話しさせていただきます。

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

読み取り専用・隠しファイル両方を取得したい場合は、上記「読み取り専用ファイルを取得したい場合」のコードDir関数の「vbReadOnly」となっている所を「vbReadOnly+vbHidden」、Getattr関数・And演算のところを「((GetAttr(pt & “\” & fn) And vbHidden) = vbHidden) Or ((GetAttr(pt & “\” & fn) And vbReadOnly) = vbReadOnly)」と変更して実行します。

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

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

標準ファイルを取得しないために、下記GetAttr関数・And演算子処理で除外します。

If ((GetAttr(pt & "\" & fn) And vbHidden) = vbHidden) Or _
    ((GetAttr(pt & "\" & fn) And vbReadOnly) = vbReadOnly) Then
End if

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

なぜこんな結果になってしまうのか。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) = 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)

この処理だけの場合、結果は標準ファイル(読み取り専用ファイル)や「.」とか「..」とか、ナゾの記号まで取得しています。
標準ファイル名を取得したい場合」の項目でお話しした通り、標準ファイルを指定しているのに、読み取り専用ファイルも取得されてしまうようなので、今回も読み取り専用ファイルが含まれています。

これらの標準ファイル(読み取り専用ファイル)を除外するために、下記GetAttr関数・And演算子処理で除外します。

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

If (GetAttr(pt & "\" & fd) And vbDirectory) = 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

実はこの時、もう一つの属性も含めて処理されています。
それが標準ファイル vbNormal 0)です。

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

フォルダを取得する場合は「vbDirectory(16)」を指定します。
この場合も 標準ファイル vbNormal0を含めて処理されます。

16 vbNormal0)+vbDirectory(16

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

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

GetAttr関数とは

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

GetAttr(ファイル・フォルダのパス)

GetAttr関数が返すのは下記 値 になります。

定数説明
vbNormal(規定値)0標準ファイル
vbReadOnly1読み取り専用ファイル
vbHidden2隠しファイル
vbSystem4システム ファイル
vbVolume8ボリューム ラベル
vbDirectory16フォルダ
vbArchive32アーカイブ
vbAlias64エイリアス ファイル

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

例えば、下記のようなフォルダで・・・(それぞれのファイルには、ファイル名と同じ属性がつけられています。)

GetAttr("C:\VBA\Dir関数検証\03_読み取り専用ファイル.xlsx")

とした場合、読み取り専用ファイル  vbReadOnly の値「1」を返します。

GetAttr("C:\VBA\Dir関数検証\01_フォルダ")

とした場合、フォルダ  vbDirectory の値「16」を返します。

GetAttr("C:\VBA\Dir関数検証\11_隠しファイル属性付きフォルダ")

フォルダ vbDirectory(16)に、隠しファイル vbHidden(2の属性もセットされている場合、合計値の「18」を返します。

しかし、合計値の「18」だけでは何の属性を含んでいるのか判断できません。

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

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

And演算子とは

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

A And B  →   A かつ B

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

A And B

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

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

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

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

左辺Aと右辺Bに同じビットが存在するときに、同じビットの値を返します。

“C:\VBA\Dir関数検証\11_隠しファイル属性付きフォルダ” が、隠しファイルの属性付きのフォルダ

GetAttr("C:\VBA\Dir関数検証\11_隠しファイル属性付きフォルダ") And vbDirectory

とした場合は「16」を返します。つまり「フォルダ」属性を含んでいることがわかります。

(GetAttr("C:\VBA\Dir関数検証\11_隠しファイル属性付きフォルダ") And vbDirectory) = vbDirectory

これらを括弧(  )で囲み上記のようなコードにすると、「vbDirectory」(16)と同じ値を返したら「True」を返します。

GetAttr("C:\VBA\Dir関数検証\11_隠しファイル属性付きフォルダ") And vbHidden

とした場合は「2」を返します。つまり「隠しファイル」属性を含んでいることがわかります。

(GetAttr("C:\VBA\Dir関数検証\11_隠しファイル属性付きフォルダ") And vbHidden) = vbHidden

これらを括弧(  )で囲み上記のようなコードにすると、「vbHidden」(2)と同じ値を返したら「True」を返します。

“C:\VBA\Dir関数検証\01_フォルダ”に「フォルダ」属性を含んでいるかどうか調べるときには、こんな感じにします。

If (GetAttr("C:\VBA\Dir関数検証\01_フォルダ") And vbDirectory) = vbDirectory Then
    MsgBox "フォルダです!"
End if

第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

引数のファイル(フォルダ)のパスが256バイトを超えるとエラーになる

Dir関数の引数のファイル(フォルダ)パスが256バイトを超えてしまうと、下記エラーが表示されるのでご注意ください。

実行時エラー '53': ファイルが見つかりません。

第1引数に空文字””を指定した場合の注意点

Dir関数は、第1引数にファイル・フォルダのパスを指定しますが、何らかの不具合があって、第1引数が空文字””になってしまったらどうなるのでしょうか。
(例:ファイルパスを参照するセルが空欄)

Dir("")

この場合は、エクセルの「既定のローカルファイルの保存場所」のパスが指定されます。第1引数を指定していないので、結果は「長さ0の文字列 ”” (空欄)」が返ってくると思ったら大間違いです。思いがけない間違いにつながるので気を付けましょう。

「既定のローカルファイルの保存場所」のパスは[ファイル]-[オプション]の[保存]でご確認ください。

【参考】

指定したフォルダ内のファイル名全てを取得(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)

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