[[20180927155709]] 『マクロでサブフォルダ内の特定ファイルを抜き出し』(まくりん) ページの最後に飛ぶ

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

 

『マクロでサブフォルダ内の特定ファイルを抜き出して保存』(まくりん)

 あるフォルダに、作業用.xlsmがあり、
 同一フォルダ内に、日付の入ったフォルダが100ほどあります。

 その中に、ノートA* ノートB*というエクセルが入っている場合と
 入っていない場合があります。

 入っている場合は、それをコピーして
 作業用.xlsmと同一フォルダにある「保存フォルダ」に
 フォルダ名の名前を付けて保存したいです。
 (ノートA_フォルダ名.xlsx)という感じです。

 マクロで実現したいのですが、なかなかうまくいきません。

Sub Sample()

    Dim stPath As String
    Dim yyFold As Object
    Dim csFold As Object
    Dim bkName As String
    Dim bkPath As String
    Dim fso As Object
    Dim yrBook As Workbook
    Dim myBook As Workbook
    Dim cnt As Long
    Dim z As Long

    Application.ScreenUpdating = False

    stPath = ThisWorkbook.Path
    bkName = "ノートA*.xls"

    Set fso = CreateObject("Scripting.FIleSystemObject")

    For Each yyFold In fso.getfolder(stPath).subfolders

        If IsNumeric(yyFold.Name) Then

            For Each csFold In yyFold.subfolders

                bkPath = csFold.Path & "\" & bkName

                If fso.FileExists(bkPath) Then

                    Set yrBook = Workbooks.Open(bkPath)
                    cnt = cnt + 1

                    yrBook.SaveAs Filename:=ThisWorkbook.Path & "\保存フォルダ\" & csFold.Name & ".xls"

                    yrBook.Close False

                End If

            Next
        End If
    Next

    Application.ScreenUpdating = True

 End Sub

どなたかご教授ください。

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


FileExistsはワイルドカードは指定できません。
Do LoopとDirを使って回すか、
For Each objFile in csFold.FilesとIf objFile.Name Like bkName thenみたいな感じで全ファイル回す必要があります。

(名無し) 2018/09/27(木) 16:08


 横から失礼します。

 参考HPです。

 Dir関数が便利です。

http://officetanaka.net/excel/vba/function/Dir.htm
(カリーニン) 2018/09/27(木) 16:11


 ワイルドカードをはずしてもうまくいきません。
 全ファイルでも問題ないのですが、、、

Sub Sample()

    Dim stPath As String
    Dim yyFold As Object
    Dim csFold As Object
    Dim bkName As String
    Dim bkPath As String
    Dim fso As Object
    Dim yrBook As Workbook
    Dim myBook As Workbook
    Dim cnt As Long
    Dim z As Long

    Application.ScreenUpdating = False

    stPath = ThisWorkbook.Path
    bkName = "*.xlsx"

    Set fso = CreateObject("Scripting.FIleSystemObject")

    For Each yyFold In fso.getfolder(stPath).subfolders

        If IsNumeric(yyFold.Name) Then

            For Each csFold In yyFold.subfolders

                bkPath = csFold.Path & "\" & bkName

                If fso.FileExists(bkPath) Then

                    Set yrBook = Workbooks.Open(bkPath)
                    cnt = cnt + 1

                    yrBook.SaveAs Filename:=ThisWorkbook.Path & "\保存フォルダ\" & csFold.Name & ".xls"

                    yrBook.Close False

                End If

            Next
        End If
    Next

    Application.ScreenUpdating = True

 End Sub
(まくりん) 2018/09/27(木) 16:18

 >カリーニンさま
 コピーしたいファイルはサブフォルダ内にあるのです。m(__)m
(まくりん) 2018/09/27(木) 16:20

いやいや、「*」がワイルドカードなので、外せてないですから。 1ファイルずつ処理しましょう。

そして、探すファイルは.xlsx形式に変わりましたが、保存するのはxls形式ですか? このままだと中身はxlsxなのに拡張子はxlsになって、開くとエラーになりませんか?

ところで、ブックを開いて別名保存するのではなく、ファイルコピーにした方が簡単だったりしませんか?
(???) 2018/09/27(木) 16:26


 保存はxlsx形式です。
 コピーで大丈夫です。
 全ファイル、サブフォルダ名でコピーしたいです。
(まくりん) 2018/09/27(木) 16:36

 すべてのファイルをコピーし、サブフォルダ名で保存する に変更しましたが、エラーになります。
どなたかご教示いただけると嬉しいです

Sub FileCopy()
Dim fsosubfolder As Object
Dim copyToFolder As String
Dim fso As Object
Dim sourceFile

 Set fso = CreateObject("Scripting.FIleSystemObject")
 stPath = ThisWorkbook.Path

For Each fsosubfolder In fso.getfolder(stPath).subfolders
Set sourceFile = fsosubfolder.Files

 For Each sourceFile In fsosubfolder
copyToFolder = ThisWorkbook.Path & "\保存フォルダ\"
fso.CopyFile sourceFile, copyToFolder
Next

Next
Set fso = Nothing
End Sub
(まくりん) 2018/09/27(木) 17:04


まず、プロシジャ名にFileCopyは付けては駄目な名前です。同名の標準命令があり、そっちが使えなくなりますから。

そして、Set sourceFile = fsosubfolder.Files しているのに、直後に For Each sourceFile In fsosubfolder って何ですか? sourceFileはフォルダ内のファイル群を代入したのなら、次のFor Eachは別のオブジェクトがsourceFileのファイル数分ループするのでしょう? 乱暴すぎます。よく考えてください。

そして、ファイル名にサブフォルダ名を加えてリネームする部分が無くなったのですが、それではただのコピーですよ?

(???) 2018/09/27(木) 17:39


 いろいろと急ぎすぎてぐちゃぐちゃでしたね。
 ご指摘ありがとうございます。
 考え直して出直します。
 すみませんでした。
(まくりん) 2018/09/27(木) 21:16

 むかーしSHFileOperationを使ったファイルコピーの勉強用に作ったモジュールがあったので、
 ひょっとしたら流用できるかな? と思うのでちょっと貼っておきますね。
 途中でホッタラカシにしちゃったし、当時とはPC環境も違うんで、アヤシイですが^^;  (んなモン貼るなって?)

 SHFileOperation実験用モジュールは↓こんな感じ(標準モジュール)

    Option Explicit
    Option Private Module
    #If VBA7 Then
    Private Type SHFILEOPSTRUCT
        hWnd As LongPtr                   ''ウィンドウハンドル
        wFunc As Long                     ''実行する操作
        pFrom As String                   ''対象ファイル名
        pTo As String                     ''目的ファイル名
        fFlags As Integer                 ''フラグ
        fAnyOperationsAborted As Long     ''結果
        hNameMappings As LongPtr          ''ファイル名マッピングオブジェクト
        lpszProgressTitle As String       ''ダイアログのタイトル
    End Type
    Private Declare PtrSafe Function SHFileOperation Lib "shell32.dll" (lpFileOp As SHFILEOPSTRUCT) As Long
    #Else
    Private Type SHFILEOPSTRUCT
        hWnd As Long                      ''ウィンドウハンドル
        wFunc As Long                     ''実行する操作
        pFrom As String                   ''対象ファイル名
        pTo As String                     ''目的ファイル名
        fFlags As Integer                 ''フラグ
        fAnyOperationsAborted As Long     ''結果
        hNameMappings As Long             ''ファイル名マッピングオブジェクト
        lpszProgressTitle As String       ''ダイアログのタイトル
    End Type
    Private Declare Function SHFileOperation Lib "shell32.dll" (lpFileOp As SHFILEOPSTRUCT) As Long
    #End If

    Private Const FO_MOVE = &H1                 '移動
    Private Const FO_COPY = &H2                 'コピー
    Private Const FO_DELETE = &H3&              '削除
    Private Const FO_RENAME = &H4               '名前を変更

    Private Const FOF_MULTIDESTFILES = &H1&     '複数ファイル指定
    Private Const FOF_SILENT = &H4&             'プログレスバー非表示
    Private Const FOF_RENAMEONCOLLISION = &H8&  '操作結果ファイルの重複名回避
    Private Const FOF_NOCONFIRMATION = &H10&    '上書き・削除の確認ダイアログを表示しない
    Private Const FOF_ALLOWUNDO = &H40&         'ごみ箱へ
    Private Const FOF_FILESONLY = &H80&         'ワイルドカード指定のみの操作
    Private Const FOF_SIMPLEPROGRESS = &H100&   'プログレスバー中にファイル名非表示
    Private Const FOF_NOCONFIRMMKDIR = &H200&   'コピー先フォルダが存在しない場合、フォルダ作成確認無し
    Private Const FOF_NOERRORUI = &H400&        'エラーのダイアログを表示しない
    Private Const FOF_NORECURSION = &H800&      'サブフォルダ再帰的処理無し

    Private ListAry() As Variant, Cnt As Long, FSO As Object

    Function FileCopyByFO(Source As Variant, ByVal Destination As Variant) As Long
    Rem ---------------------------------------------------------------------------------------------------------
    Rem Source      フルパスで指定(ファイル名にはワイルドカード使用可)
    Rem             フルパスを格納した配列で複数指定しても可
    Rem Destination フルパスで指定した方が無難(ファイル名だけだとカレントディレクトリに行くっぽいけど)
    Rem             フルパスを格納した配列で指定しても可
    Rem             Sourceが配列でDestinationが1箇所指定だったら、Destinationにファイルが集まる
    Rem             両方配列だったら各配列の対応する要素間でコピーする
    Rem ---------------------------------------------------------------------------------------------------------
        Dim ShOPs As SHFILEOPSTRUCT, Flg As Long
        Dim SourceStr As String, DestStr As String

        Flg = FOF_NOCONFIRMATION + FOF_NOCONFIRMMKDIR '重複名回避+フォルダ作成確認無し

        If IsArray(Source) Then 'Sourceが配列だったら\0でjoin
            SourceStr = Join(Source, vbNullChar)
    '        SourceStr = SourceStr & String(2, vbNullChar) '最後に\0\0を付け加えろという話だったがエラーになった
        Else
            SourceStr = CStr(Source)
        End If
        If IsArray(Destination) Then 'Destinationが配列だったら\0でjoinして、
            DestStr = Join(Destination, vbNullChar)
            Flg = Flg + FOF_MULTIDESTFILES '複数ファイル指定フラグを加える
        Else
            DestStr = CStr(Destination)
        End If
        With ShOPs
            .hWnd = 0
            .wFunc = FO_COPY
            .pFrom = SourceStr
            .pTo = DestStr
            .fFlags = Flg
        End With

        FileCopyByFO = SHFileOperation(ShOPs)

    End Function

    Function ListFilesBy(ResList() As Variant, ByVal TargetPath As String, ByVal Mask As String, Optional ModifiedLimit As Date) As Long
    Rem ---------------------------------------------------------------------------------------------------------
    Rem TargetPath配下(サブフォルダ含む)からMaskに一致するファイル名のフルパスをResListに入れて返す
    Rem 戻り値は一致したファイル数
    Rem ModifiedLimitはファイル更新日の下限を指定するオプション
    Rem ---------------------------------------------------------------------------------------------------------
        Cnt = 0
        Erase ListAry
        Set FSO = CreateObject("Scripting.FileSystemObject")
        If FSO.FolderExists(TargetPath) Then
            Call FoldersListup(TargetPath, Mask, ModifiedLimit)
        End If
        ResList = ListAry
        ListFilesBy = Cnt
        Set FSO = Nothing
    End Function
    Rem 以下Private処理==========================================================================================
    Private Sub FoldersListup(Path As String, Mask As String, ModifiedLimit As Date)
    Rem サブフォルダ内のファイル探索を再帰的に実行
        Dim aFolder As Object
        For Each aFolder In FSO.GetFolder(Path).SubFolders
            Call FoldersListup(aFolder.Path, Mask, ModifiedLimit)
        Next
        Call FilesListup(Path, Mask, ModifiedLimit)
    End Sub
    Private Sub FilesListup(Path As String, Mask As String, ModifiedLimit As Date)
    Rem Dir関数使わない理由は特にないんだが・・・気分で
        Dim aFile As Object
        For Each aFile In FSO.GetFolder(Path).Files
            If StrConv(aFile.Name, vbLowerCase) Like StrConv(Mask, vbLowerCase) Then
                If aFile.DateLastModified >= ModifiedLimit Then
                    Cnt = Cnt + 1
                    ReDim Preserve ListAry(1 To Cnt)
                    ListAry(Cnt) = aFile.Path
                End If
            End If
        Next
    End Sub

 で、 別の標準モジュールでこんな風に書いてみるという・・・
 (とりあえず「ノートA*」の場合だけですけど)

    Sub test()
        Dim r() As Variant, c As Long, w() As Variant, i As Long, p As String
        c = ListFilesBy(r, ThisWorkbook.Path, "ノートA*")
        If c = 0 Then Exit Sub
        w = r
        With CreateObject("Scripting.FileSystemObject")
            For i = LBound(w) To UBound(w)
                p = "_" & .GetFolder(.GetParentFolderName(w(i))).Name
                w(i) = .GetBaseName(w(i)) & p & "." & .GetExtensionName(w(i))
                w(i) = ThisWorkbook.Path & "\保存フォルダ\" & w(i)
            Next
        End With
        FileCopyByFO r, w
    End Sub

(白茶) 2018/09/27(木) 21:57


コメント返信:

[ 一覧(最新更新順) ]


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