89本目 二つのフォルダの統合

VBA100本ノック

VBA100本ノック 89本目を動画で解説しています。Excelの神髄さんの模範解答はコチラ


問題

#VBA100本ノック 89本目
フォルダ「A」とフォルダ「B」を統合して、
フォルダ「C」を作成する。
全サブフォルダの、全ファイルを対象としてください。
同一フォルダに同一ファイル名となる場合は、
より更新日時の新しいファイルを採用してください。
同一更新日時の場合はどちらでも良い。
※パスは任意で

解答

Sub ノック89本目()
    Dim wb As Workbook: Set wb = ThisWorkbook
    Dim sPathA As String: sPathA = wb.Path & "\A"
    Dim sPathB As String: sPathB = wb.Path & "\B"
    Dim sPathC As String: sPathC = wb.Path & "\C"
    Dim objFSO As New FileSystemObject
    
    If objFSO.FolderExists(sPathC) Then
        objFSO.DeleteFolder sPathC 'Cフォルダがあれば削除
    End If
    objFSO.CopyFolder sPathA, sPathC 'とりあえずAをCの名前で複製
    
    Call 再帰(sPathB, sPathB, sPathC, objFSO) '元ファイル名,fsoを渡しつつ呼び出し
    
End Sub

Sub 再帰(元F名 As String, 元々BFol名 As String, 元々CFol名 As String, objFSO As FileSystemObject)
    
'Cフォルダに元Fol名のフォルダが無ければフォルダごとコピー
    Dim F名チェック用  As String
    F名チェック用 = Replace(元F名, 元々BFol名, 元々CFol名)
    If Not objFSO.FolderExists(F名チェック用) Then
        objFSO.CopyFolder 元F名, F名チェック用 'もし無ければコピー。
        Exit Sub  'それ以下のフォルダは再帰しない
    End If

'Cフォルダに元のファイル名と同名のファイル無ければコピー。あれば更新時刻を比較
    Dim f As File, File名チェック用 As String
    For Each f In objFSO.GetFolder(元F名).Files
        File名チェック用 = F名チェック用 & "\" & f.Name
        If Not objFSO.FileExists(Fileチェック用) Then  'もし同名のファイルがなければ
            objFSO.CopyFile 元F名 & "\" & f.Name, Fileチェック用 'ファイルをコピー
        Else
            If f.DateLastModified >= objFSO.GetFile(Fileチェック用).DateLastModified Then 'Bの更新が新しければ
                objFSO.CopyFile 元F名 & "\" & f.Name, Fileチェック用  'ファイルをコピー
            End If
        End If
    Next
    
'サブフォルダを再帰呼び出し
    Dim 元Fol As Folder
    For Each 元Fol In objFSO.GetFolder(元F名).SubFolders
        Call 再帰(元Fol.Path, 元々BFol名, 元々CFol名, objFSO)
    Next
    
End Sub

■考え方・流れ
0:00 冒頭・問題確認
2:12 変数の宣言、コードの流れを考察 
4:56 再帰用のプロシージャ作成
6:28 再帰のコード考察
8:27 コピー先にフォルダがなければフォルダごとコピー
12:11 更新時刻が新しいか、コピー先にフォルダがなければコピーする

今回も投稿主の語彙力と説明力のなさが光っております!

新人君
新人君

89本やってきてこれは
ヤバいですね!

ブチョ
ブチョ

煽りツイートやめて?

新人君
新人君

確かに煽りはよくない…

変数の宣言、コードの流れを考察

66本目の再帰のコードを引用して使っていきます!

ファイルコピー、フォルダコピーなども66本目で使った
FSOさんをそのまま使用!
変数名はobjFSO。

考え方はこんな感じ。

1.”C”フォルダがあれば削除。
2.”A”フォルダをコピーして”C”フォルダに名前変更。
3.”B”ファルダを再帰してコピーしていく。

フォルダの削除はfsoのDeleteFileメソッドさん!

フォルダのコピーはCopyFolderメソッドさんを使えば簡単!

この辺りがわからないという方は、ノック20本目を見返してね!

再帰用のプロシージャ作成

※一旦、引数でフォルダを受け渡すようにしてます。
この後で思い返し、フォルダ名(String)を受け渡した方が簡単ということに気づきましたww

別プロシージャの呼び出しは、Callステートメントさんを使えば良かったね!

ついでに、FSOさんも引数で受け渡しちゃいましょう!

こんな感じ!
これでSub 再帰でもFSOさんが使えるね!

ここまでは大丈夫かな?

再帰のコード考察

再帰の考え方に入っていきましょう!
だけど…考えれば考えるほど…「フォルダ」じゃなくて「フォルダ名」を
受け渡す方がいいことに気づく…ww
許して下せえ!

再帰は66本目でやったように、For Eachさんを使います。
受け取ったフォルダの中の、サブフォルダを回していくイメージ!

一旦はここまで!

コピー先にフォルダがなければフォルダごとコピー

ココからは少し、処理速度とかを鑑みて、
不要な処理はしないようにしていくよー!

コピー先に、引数で受け取ったフォルダ名のフォルダがなければ、フォルダごとコピーすればええやん。
いちいちそのフォルダの中のサブフォルダを再帰…とかってしなくてええやん。
大元のフォルダをコピーすればええんやから。って話。

まずはコメントから書いていくといいかも…

フォルダがあるかどうかを判定したいから…

元々はフォルダ”A”だった部分を、新しい”C”に置換してあげて比較!

ムズすぎて吐きそう…

更新時刻が新しいか、コピー先にフォルダがなければコピーする

最後は同名のフォルダがあった場合、それぞれの中身のファイルの更新時刻を判定!

ファイルの更新時刻を調べたい場合は、FSOさんのFileExistsを使えばいいね!

デカかった方を残せばOK

こ、これで完成かな…

ホントにお疲れさまでござんした…

ブチョ
ブチョ

お疲れ様!
ポケモン発売だね!

新人君
新人君

急に何すか!

あとがたり

おはこんばんちは。uぷ主です。
89本目、二つのフォルダを統合する問題でした…

いや~、再帰、また来ちゃったか~!って感じでした…w
頭がおかしくなりそうになります。

でも実は…LAMBDA関数が出てから…
pythonでちょこっと勉強したのでw
ある程度やりたいことはできるようになってきたかな!

フォルダとか、階層チックな構造の処理をするときには
知ってて損はないですよね。

再帰を知るまでは、とりあえずエラー処理入れて、
1から100フォルダ分ループして…みたいにやったことありましたが…

処理に意味わからんぐらい時間かかってたもんなぁ…

いや~、ちゃんと学ぶって大事w
今回も超勉強になりました。
ありがとうございました。


あ、そうそう、いよいよポケモンSV発売されましたね。
ちょっと最近、本業が忙しいんで…まだ買えてないんですが…

買います。そのうち。
スカーレットかな~!

全国のExcelとポケモン好きのみなさん、ぜひ友達になって下さい!
以上!

最後までご視聴いただいたみなさん!ありがとうございました!

コメント

タイトルとURLをコピーしました