[[20200626112757]] 『ファイル名の取得』(いちご) ページの最後に飛ぶ

[ 初めての方へ | 一覧(最新更新順) | 全文検索 | 過去ログ ]

 

『ファイル名の取得』(いちご)

ファイルの管理、確認するのに一覧を作ってます
自動でやりたくてサンプルコードを見つけました
セルに書き出すにはどうやったらいいですか?

フォルダパスはB2にファイル一覧はB5セルから書き出したいです

'--- 指定フォルダ内に含まれるすべてのファイルパスを取得 ---'
Public Sub GetAllFilePath()

    '--- ファイル一覧を取得したいフォルダのパス ---'
    Dim folderPath As String
    folderPath = "[フォルダパス]"

    '--- サブフォルダのパス一覧を取得 ---'
    Dim folderList As Variant
    folderList = GetFolderPath(folderPath)

    '--- 指定フォルダのファイルパスを取得
    Dim fileList() As String
    fileList = GetFileListFso(folderPath)

    '--- ファイル数を保存する変数 ---'
    Dim n As Long
    Dim m As Long

    '--- サブフォルダのファイルパスを取得し順次結合する
    Dim tmpList() As String
    Dim i As Long
    Dim s As Variant
    For Each s In folderList

        tmpList = GetFileListFso(CStr(s))

        n = GetArrayLength(fileList)
        m = GetArrayLength(tmpList)

        If (0 < n + m) Then
            ReDim Preserve fileList(1 To n + m)
            For i = 1 To m
                fileList(n + i) = tmpList(i)
            Next i
        End If
    Next s

End Sub

'--- 1次元配列の要素数を取得する関数 ---'
Public Function GetArrayLength(vList As Variant) As Long

    Dim n As Long

    If (IsEmptyArray(vList)) Then
        n = 0
    Else
        n = UBound(vList)
    End If

    GetArrayLength = n

End Function

'--- フォルダに含まれる(サブフォルダ除く)のファイル一覧を取得する関数 ---'
Public Function GetFileListFso(folderPath As String) As String()

    '--- ファイルシステムオブジェクト ---'
    Dim fso As Object
    Set fso = CreateObject("Scripting.FileSystemObject")

    '--- ファイル数を格納する変数 ---'
    Dim n As Variant
    n = fso.GetFolder(folderPath).Files.Count

    If (0 < n) Then
        '--- ファイル名を格納する配列 ---'
        Dim str() As String
        ReDim str(1 To n)

        '--- ファイル名を格納 ---'
        i = 1
        Dim f As Object
        For Each f In fso.GetFolder(folderPath).Files
            str(i) = f.Path
            i = i + 1
        Next f
    End If

    GetFileListFso = str

End Function

'--- サブフォルダを再帰的に取得する関数 ---'
Public Function GetFolderPath(folderPath As String) As String()

    '--- ファイルシステムオブジェクト ---'
    Dim fso As Object
    Set fso = CreateObject("Scripting.FileSystemObject")

    '--- フォルダ数を格納する変数 ---'
    Dim n As Variant
    n = fso.GetFolder(folderPath).SubFolders.Count

    If (0 < n) Then
        '--- フォルダパスを格納する配列 ---'
        Dim str() As String
        ReDim str(1 To n)

        '--- フォルダパスを格納 ---'
        Dim i As Long
        Dim j As Long
        Dim m As Long
        i = 1
        Dim strTmp() As String

        'フォルダパスを指定してすべてのサブフォルダを取得
        Dim f As Object
        For Each f In fso.GetFolder(folderPath).SubFolders
            str(i) = f.Path

            strTmp = GetFolderPath(str(i))  '再帰的呼び出し
            If (Not IsEmptyArray(strTmp)) Then
                m = UBound(strTmp, 1)
            Else
                m = 0
            End If

            'サブフォルダ内にさらにフォルダがあればその分だけ配列を拡張
            n = UBound(str, 1)
            ReDim Preserve str(1 To n + m)
            For j = 1 To m
                str(i + j) = strTmp(j)
            Next j

            i = i + m + 1
        Next f
    End If

    GetFolderPath = str

End Function

'--- 配列が空かどうかを判定する関数 ---'
Public Function IsEmptyArray(arrayTmp As Variant) As Boolean
On Error GoTo ERROR_

    If (0 < UBound(arrayTmp, 1)) Then
        IsEmptyArray = False
    End If

    Exit Function

ERROR_:

    IsEmptyArray = True

End Function

< 使用 Excel:Excel2019、使用 OS:Windows10 >


>セルに書き出すにはどうやったらいいですか?
配列に格納するんじゃなくてセルに書き出したらよいのでは?

なお、再帰処理の話は別にして↓が参考になると思いますので確認してみてはどうでしょうか?
http://officetanaka.net/excel/vba/file/file07.htm

再帰処理も含めてみるなら↓かも?
http://officetanaka.net/excel/vba/tips/tips36.htm

(もこな2 ) 2020/06/26(金) 11:45


>配列に格納するんじゃなくてセルに書き出したらよいのでは?

まだマクロ勉強中でよくわかってません
格納したやつを書きだすのとは違うってことなんでしょうか?
では、
'--- 指定フォルダ内に含まれるすべてのファイルパスを取得 ---'
'--- 1次元配列の要素数を取得する関数 ---'
この後に書き出すものを作成したらいいのでしょうか?

(いちご) 2020/06/26(金) 12:42


もこな2さんの教えサイトを参照しましたが
やっぱりセルに書き出すところがわからないです
どうやったら書き出せるようになるか教えてくれませんか?

Sub Sample()

    Call FileSearch("C:\Sample")
End Sub

Sub FileSearch(Path As String)

    Dim FSO As Object, Folder As Variant, File As Variant
    Set FSO = CreateObject("Scripting.FileSystemObject")
    For Each Folder In FSO.GetFolder(Path).SubFolders
        Call FileSearch(Folder.Path)
    Next Folder
    For Each File In FSO.GetFolder(Path).Files
        Debug.Print File.Path
    Next File
End Sub

(いちご) 2020/06/26(金) 12:56


「2020/06/26(金) 12:56」に提示されたコードはステップ実行して動きを確認してみましたか?

どの部分が出力にかかわってくるかわかっているかどうかで説明が変わるので、そこが理解できているのか教えてください。

また、↓も読みましたか?
http://officetanaka.net/excel/vba/file/file07.htm

(もこな2 ) 2020/06/26(金) 13:04


>「2020/06/26(金) 12:56」に提示されたコードはステップ実行して動きを確認してみましたか?

ステップ実行で確認しましたが恥ずかしながら動きを見てもわからなかったです

教えてもらったサイトは両方とも読みましたし、試してもみましたが
何も起こらなかったです
両方とも書き出すよう追加しなくちゃいけないんでしょうか?

http://officetanaka.net/excel/vba/file/file07.htm

このようにセルにファイル(できればフォルダも)を書き出したいです

(いちご) 2020/06/26(金) 14:47


横合いから失礼。
 
うーん。
提示されたコードが理解できるなら、
配列をシートに書き出すのはできるはずなんですが・・・。
 
ということは、学習の順序が違うというか、つまみ食い的にコードを切り貼りしても
なかなか上手く使えるようにはならない、ということです。
一念発起して、基本的なことを順次学習するのが、かえって近道だと思います。
薄いもので結構ですので、一冊の書籍をとおして学習することをお薦めします。
 
さはさりながら、目前のものに対応する必要があるとすれば、 
例えば、↓こんな説明も分かり易いかもしれません。
 
https://www.moug.net/tech/exvba/0100049.html
配列をシートに書き出す手法が書かれていますから、これを応用してみてください。
つまり、Sub GetAllFilePath()の最後に、書き出すコードを追加するのです。
 
folderList サブフォルダたちからなる配列
fileList すべてのファイルパスの配列
ということは理解されているんですね?

(γ) 2020/06/28(日) 06:19


だらだらと書いていたら2日ほど経っちゃいましたが投稿しておきます。

>何も起こらなかったです
何も起こらないことはないと思いますけど・・・・該当のフォルダやファイルが無かったなんてオチはないですよね?

■1
たとえば、A1セルに「ああ」とマクロで書き込むなら↓ですよね?

 Range("A1").Value = "ああ"

これを紹介した事例ではCellsプロパティを使って↓のようにしています。

 Cells(1,1).Value = "ああ"

注意点として↓のように行・列の順番が違います
 Rangeプロパティ・・・[列文字]と[行番号]を組み合わせて記述
 Cellsプロパティ・・・[行番号]と[列番号(例外的に列文字でもOK)]をそれぞれ指定するように記述

ここまでは理解されてますか?理解してから読み進めてください。

■2
今回は、セルに出力したいのですから↓ではちょっとまずいですよね?

 ↓配列に格納している
 str(i) = f.Path

 ↓イミディエイトに出力している
 Debug.Print File.Path

なので、例えば↓のようにセルに書き込めばいいじゃないですかと言っていたわけです。

 Cells(1,1).Value.Value = 書き込みたい内容

ただ、上記のように行も列固定してしまっては、毎回上書きされてしまうので実際には一定の間隔でずれていってほしいですよね。
それにはどうしたらよいか考えてみてください。



答えは簡単、書き込むごとに行番号に加算していけばいいんです。
なので紹介したサイトでは、書き込む直前に行番号に使っている変数「cnt」に1を足しているのです。

 cnt = cnt + 1
 Cells(cnt, 1) = buf

ここまでもよろしいですか?次からややこしくなりますので、分からない場合は戻って再度読んだり、聞いたりしてください。

■3
最初に提示されたコードでは再帰処理というものをしています。

 〜★〜 ↓は最初に提示されたコードがなぜ再帰処理をしているかわからない場合のみ読んでください 〜★〜 

再帰処理とは自分自身を呼び出す処理です。
なぜそのようなことをしているかというと、ずばり「サブフォルダ」まで対象にしたいためです。
さらにサブフォルダの配下にもサブフォルダ(元のフォルダからみると孫フォルダ、ひ孫フォルダ・・・)があるかもしれませんからサブフォルダの下にサブフォルダがある限りどんどん下がって調べているわけです。

さて、再帰処理は自分自身を呼び出すと書きました。
しかし、呼び出したものと親元は、あくまで別のプロシージャとして扱われますから、プロシージャ内で宣言した変数もそれぞれ別扱いになります。

そうなると、例えば「cnt」をプロシージャごとに宣言してしまうと、フォルダが切り替わるたびに1行目から出力するようになってしまいマいことになります。
これを回避するために、「cnt」は1つのプロシージャではなく、複数のプロシージャから使う変数として設定してあげる必要があるわけです。
http://officetanaka.net/excel/vba/variable/05.htm

■4
ということで、"配列を使わずに"1処理ずつセルに出力するならこんな感じです。

    Option Explicit
    Dim cnt As Long
    '-------------------------------------------------
    Sub メインルーチンA()
        cnt = 1
        Dim フォルダパス As String
        フォルダパス = "C:\Test"
        Call サブルーチンA(CreateObject("Scripting.FileSystemObject").GetFolder(フォルダパス))
    End Sub
    '--------------------------------------------------
    Sub サブルーチンA(フォルダ As Object)
        Dim サブフォルダ As Object
        Dim ファイル As Object
        Dim フラグ As Boolean

        For Each サブフォルダ In フォルダ.SubFolders
            Call サブルーチンA(サブフォルダ)
        Next サブフォルダ

        For Each ファイル In フォルダ.Files
            If Not フラグ Then
                Cells(cnt, "A").Value = フォルダ.Path
                フラグ = True
            End If
            Cells(cnt, "B").Value = ファイル.Name
            cnt = cnt + 1
        Next
    End Sub

(もこな2 ) 2020/06/28(日) 08:32


■5
さらに、「■4」ではモジュールレベル変数を使う方法を紹介しましたが、ほかにも変数をほかのプロシージャに渡すという方法もあります。
(実は「■4」でも「フォルダ」という変数をサブルーチンに渡すようにしています)
http://officetanaka.net/excel/vba/tips/tips94.htm

これが理解できれば、呼び出した最初のプロシージャに戻ってきたときに、配列に格納されているファイルパス?を一気に出力すればいいのはわかりますよね?
(出力する方法はγさんのコメントを参照)

    Sub メインルーチンB()
        Dim 配列 As Variant
        Dim フォルダパス As String
        フォルダパス = "C:\Test"
        ReDim 配列(0)

        Call サブルーチンB(CreateObject("Scripting.FileSystemObject").GetFolder(フォルダパス), 配列)

        '▼ここで一気に出力
         Range("A1").Resize(UBound(配列)).Value = WorksheetFunction.Transpose(配列)
    End Sub
    '--------------------------------------------------
    Sub サブルーチンB(フォルダ As Object, 配列)
        Dim サブフォルダ As Object
        Dim ファイル As Object

        For Each サブフォルダ In フォルダ.SubFolders
            Call サブルーチンB(サブフォルダ, 配列)
        Next サブフォルダ

        For Each ファイル In フォルダ.Files
            配列(UBound(配列)) = ファイル.Path
            ReDim Preserve 配列(UBound(配列) + 1)
        Next
    End Sub

■6
ちなみに、サブフォルダを含めたファイル名一覧を取得したいのであれば、FSOを使わずにVBA上でコマンドプロンプトのDIRコマンドを実行しその結果を利用するといった方法もあります。

 (そちらの方法の説明は苦手なので、過去ログを紹介するだけにとどめておきます。)
[[20200616124543]] 『フォルダ名の取得』
[[20200206165234]] 『ブック内の値をシート名を含む他のブックを指定フォルダの中から検索して開きたい』(我論)
[[20191111110431]] 『フォルダ名の全ファイル名を取得する』(りんご)

(もこな2 ) 2020/06/28(日) 09:02


もこな2さん、とても分かりやすかったです
■4まで理解できました
■5はもう少し時間をください

それで練習をかねて■4のコードを
B列が最下層、C列、D列とひとつづつ上層階層に上がっていくように、
A列にフルパス、B列にファイル名、C列〜フォルダ名とやってみました

C列まではできたのですがD列以降の上層階層にあるフォルダ名をどうやったら取得できるかが
できれば教えてくれませんか?
フルパスを使ってMID関数とかと思ったんですが文字数の割り出しができなかったです

(いちご) 2020/06/29(月) 13:14


 Option Explicit
    Dim cnt As Long
    '-------------------------------------------------
    Sub メインルーチンA()
        cnt = 5
        Dim フォルダパス As String
        フォルダパス = Sheets("テスト").Range("C2")
        Call サブルーチンA(CreateObject("Scripting.FileSystemObject").GetFolder(フォルダパス))
    End Sub
    '--------------------------------------------------
    Sub サブルーチンA(フォルダ As Object)
        Dim サブフォルダ As Object
        Dim ファイル As Object
        Dim フラグ As Boolean

        For Each サブフォルダ In フォルダ.SubFolders
            Call サブルーチンA(サブフォルダ)
        Next サブフォルダ

        For Each ファイル In フォルダ.Files
          If Not フラグ Then
                Sheets("作業シート").Cells(cnt, "A").Value = フォルダ.Path
                フラグ = True
            End If
             Sheets("作業シート").Cells(cnt, "B").Value = ファイル.Name
               cnt = cnt + 1
        Next

        For Each サブフォルダ In フォルダ.SubFolders
            If Not フラグ Then
                Sheets("作業シート").Cells(cnt, "A").Value = サブフォルダ.Path
                フラグ = True
            End If
            Sheets("作業シート").Cells(cnt, "C").Value = サブフォルダ.Name
            'Sheets("作業シート").Cells(cnt, "D").Value = フォルダ.ParentFolder.Name
            cnt = cnt + 1
        Next

        sfp = Len(cPath) + 1

    End Sub

 コードのっけるの忘れてました、ごめんなさい

(いちご) 2020/06/29(月) 13:22


コメント返信:

[ 一覧(最新更新順) ]


YukiWiki 1.6.7 Copyright (C) 2000,2001 by Hiroshi Yuki. Modified by kazu.