VBAを使用してフォルダー内のファイルをループしますか?


236

を使用してディレクトリのファイルをループしたい Excel 2010で。

ループでは、私は必要になります:

  • ファイル名、および
  • ファイルがフォーマットされた日付。

フォルダーに50個以下のファイルがある場合は正常に機能する次のコードをコーディングしました。このコードの唯一の問題は、検索する操作にfile.name非常に長い時間がかかることです。

機能するが、速度が遅すぎるコード(100ファイルあたり15秒):

Sub LoopThroughFiles()
   Dim MyObj As Object, MySource As Object, file As Variant
   Set MySource = MyObj.GetFolder("c:\testfolder\")
   For Each file In MySource.Files
      If InStr(file.name, "test") > 0 Then
         MsgBox "found"
         Exit Sub
      End If
   Next file
End Sub

問題が解決しました:

  1. 私の問題はDir、特定の方法(15000ファイルで20秒)を使用し、コマンドを使用してタイムスタンプをチェックする以下の解決策によって解決されましたFileDateTime
  2. 20秒未満からの別の回答を考慮すると、1秒未満に短縮されます。

まだVBAの初期時間が遅いようです。Application.ScreenUpdating = falseを使用していますか?
Michiel van der Blonk 2015年

2
codeSet MyObj = New FileSystemObject
baldmosher '25 / 01/25

13
人々がすぐにFSOを「遅い」と呼ぶのはかなり悲しいですが、に対する遅延バインディングの代わりに早期バインディングを使用するだけで回避できるパフォーマンスの低下については誰も言及していませんObject
Mathieu Guindon 2017

回答:


46

代わりに関数としての私の解釈は次のとおりです。

'#######################################################################
'# LoopThroughFiles
'# Function to Loop through files in current directory and return filenames
'# Usage: LoopThroughFiles ActiveWorkbook.Path, "txt" 'inputDirectoryToScanForFile
'# /programming/10380312/loop-through-files-in-a-folder-using-vba
'#######################################################################
Function LoopThroughFiles(inputDirectoryToScanForFile, filenameCriteria) As String

    Dim StrFile As String
    'Debug.Print "in LoopThroughFiles. inputDirectoryToScanForFile: ", inputDirectoryToScanForFile

    StrFile = Dir(inputDirectoryToScanForFile & "\*" & filenameCriteria)
    Do While Len(StrFile) > 0
        Debug.Print StrFile
        StrFile = Dir

    Loop

End Function

25
何も返されないのに、なぜ関数なのか?関数で囲まれていることを除いて、brettdjの回答と同じではありません
Shafeek

253

Dirワイルドカードを使用するため、事前にフィルターを追加し、test各ファイルのテストを回避する大きな違いを生み出すことができます

Sub LoopThroughFiles()
    Dim StrFile As String
    StrFile = Dir("c:\testfolder\*test*")
    Do While Len(StrFile) > 0
        Debug.Print StrFile
        StrFile = Dir
    Loop
End Sub

29
すごい。これにより、実行時間が20秒から1秒未満に改善されました。コードはかなり頻繁に実行されるため、これは大きな改善です。ありがとうございました!!
tyrex 2012

それは、Do while ...ループがwhile ... wendよりも優れているためかもしれません。ここでの詳細情報はstackoverflow.com/questions/32728334/...
ヒラDG

6
私はその改善レベル(20-xxx回)では考えていません-ワイルドカードが違いを生むと思います。
brettdj 2016年

DIR()は隠しファイルを返さないようです。
18年

@hamish、あなたがファイル(隠し、システムなど)の異なるタイプを返すために、引数を変更することができます- MSのドキュメントを参照してください。docs.microsoft.com/en-us/office/vba/language/reference/...
ヴィンセント

158

Dirは非常に速いようです。

Sub LoopThroughFiles()
    Dim MyObj As Object, MySource As Object, file As Variant
   file = Dir("c:\testfolder\")
   While (file <> "")
      If InStr(file, "test") > 0 Then
         MsgBox "found " & file
         Exit Sub
      End If
     file = Dir
  Wend
End Sub

3
ありがとうございます。私はDirを使用しますが、そのように使用できることを知りませんでした。さらに、コマンドでFileDateTime私の問題は解決されます。
tyrex 2012

4
まだ1つの質問。DIRが最新のファイルからループする場合、速度を大幅に向上させることができます。これを行う方法はありますか?
tyrex 2012

3
後者の質問は、brettdjからの以下のコメントによって解決されました。
tyrex 2012

監督はなりますnottraverse the whole directory tree。場合に必要:analystcave.com/vba-dir-function-how-to-traverse-directories/...
AnalystCave.com

Dirは他のDirコマンドによっても中断されるため、Dirを含むサブルーチンを実行すると、元のサブルーチンでそれを「リセット」できます。元の質問に従ってFSOを使用すると、この問題が解消されます。編集:ちょうど@LimaNightHawkの投稿を見たところ、同じこと
ボールドモシャー

26

Dir関数は進むべき道ですが、問題は、Dirここで述べように、関数を再帰的使用できないことです

これを処理する方法は、Dir関数を使用してターゲットフォルダーのすべてのサブフォルダーを取得し、それらを配列にロードして、その配列を再帰する関数に渡すことです。

これを実現するために私が作成したクラスは次のとおりです。これには、フィルターを検索する機能が含まれています。(ハンガリー記法を許さなければならないでしょう。これは大流行したときに書かれました。

Private m_asFilters() As String
Private m_asFiles As Variant
Private m_lNext As Long
Private m_lMax As Long

Public Function GetFileList(ByVal ParentDir As String, Optional ByVal sSearch As String, Optional ByVal Deep As Boolean = True) As Variant
    m_lNext = 0
    m_lMax = 0

    ReDim m_asFiles(0)
    If Len(sSearch) Then
        m_asFilters() = Split(sSearch, "|")
    Else
        ReDim m_asFilters(0)
    End If

    If Deep Then
        Call RecursiveAddFiles(ParentDir)
    Else
        Call AddFiles(ParentDir)
    End If

    If m_lNext Then
        ReDim Preserve m_asFiles(m_lNext - 1)
        GetFileList = m_asFiles
    End If

End Function

Private Sub RecursiveAddFiles(ByVal ParentDir As String)
    Dim asDirs() As String
    Dim l As Long
    On Error GoTo ErrRecursiveAddFiles
    'Add the files in 'this' directory!


    Call AddFiles(ParentDir)

    ReDim asDirs(-1 To -1)
    asDirs = GetDirList(ParentDir)
    For l = 0 To UBound(asDirs)
        Call RecursiveAddFiles(asDirs(l))
    Next l
    On Error GoTo 0
Exit Sub
ErrRecursiveAddFiles:
End Sub
Private Function GetDirList(ByVal ParentDir As String) As String()
    Dim sDir As String
    Dim asRet() As String
    Dim l As Long
    Dim lMax As Long

    If Right(ParentDir, 1) <> "\" Then
        ParentDir = ParentDir & "\"
    End If
    sDir = Dir(ParentDir, vbDirectory Or vbHidden Or vbSystem)
    Do While Len(sDir)
        If GetAttr(ParentDir & sDir) And vbDirectory Then
            If Not (sDir = "." Or sDir = "..") Then
                If l >= lMax Then
                    lMax = lMax + 10
                    ReDim Preserve asRet(lMax)
                End If
                asRet(l) = ParentDir & sDir
                l = l + 1
            End If
        End If
        sDir = Dir
    Loop
    If l Then
        ReDim Preserve asRet(l - 1)
        GetDirList = asRet()
    End If
End Function
Private Sub AddFiles(ByVal ParentDir As String)
    Dim sFile As String
    Dim l As Long

    If Right(ParentDir, 1) <> "\" Then
        ParentDir = ParentDir & "\"
    End If

    For l = 0 To UBound(m_asFilters)
        sFile = Dir(ParentDir & "\" & m_asFilters(l), vbArchive Or vbHidden Or vbNormal Or vbReadOnly Or vbSystem)
        Do While Len(sFile)
            If Not (sFile = "." Or sFile = "..") Then
                If m_lNext >= m_lMax Then
                    m_lMax = m_lMax + 100
                    ReDim Preserve m_asFiles(m_lMax)
                End If
                m_asFiles(m_lNext) = ParentDir & sFile
                m_lNext = m_lNext + 1
            End If
            sFile = Dir
        Loop
    Next l
End Sub

列にあるファイルを一覧表示したい場合、これをどのように実装できますか?
jechaviz 2014

@jechaviz GetFileListメソッドは、文字列の配列を返します。おそらく、配列を反復処理して、アイテムをListViewなどに追加するだけです。リストビューでアイテムを表示する方法の詳細は、おそらくこの投稿の範囲を超えています。
LimaNightHawk 2014

6

Dir 他のフォルダーからのファイルを処理するとき、関数はフォーカスを簡単に失います。

コンポーネントでより良い結果を得ましたFileSystemObject

完全な例をここに示します。

http://www.xl-central.com/list-files-fso.html

Visual Basic EditorでMicrosoft Scripting Runtimeへの参照を設定することを忘れないでください([ツール]> [参照]を使用)。

試してみる!


技術的には、これは質問者が使用している方法です。参照が含まれていないため、この方法が遅くなります。
Marcucciboy2

-2

これを試してみてください。(リンク

Private Sub CommandButton3_Click()

Dim FileExtStr As String
Dim FileFormatNum As Long
Dim xWs As Worksheet
Dim xWb As Workbook
Dim FolderName As String
Application.ScreenUpdating = False
Set xWb = Application.ThisWorkbook
DateString = Format(Now, "yyyy-mm-dd hh-mm-ss")
FolderName = xWb.Path & "\" & xWb.Name & " " & DateString
MkDir FolderName
For Each xWs In xWb.Worksheets
    xWs.Copy
    If Val(Application.Version) < 12 Then
        FileExtStr = ".xls": FileFormatNum = -4143
    Else
        Select Case xWb.FileFormat
            Case 51:
                FileExtStr = ".xlsx": FileFormatNum = 51
            Case 52:
                If Application.ActiveWorkbook.HasVBProject Then
                    FileExtStr = ".xlsm": FileFormatNum = 52
                Else
                    FileExtStr = ".xlsx": FileFormatNum = 51
                End If
            Case 56:
                FileExtStr = ".xls": FileFormatNum = 56
            Case Else:
                FileExtStr = ".xlsb": FileFormatNum = 50
        End Select
    End If
    xFile = FolderName & "\" & Application.ActiveWorkbook.Sheets(1).Name & FileExtStr
    Application.ActiveWorkbook.SaveAs xFile, FileFormat:=FileFormatNum
    Application.ActiveWorkbook.Close False
Next
MsgBox "You can find the files in " & FolderName
Application.ScreenUpdating = True

End Sub
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.