使用VBA循环浏览文件夹中的文件?

Posted

技术标签:

【中文标题】使用VBA循环浏览文件夹中的文件?【英文标题】:Loop through files in a folder using VBA? 【发布时间】:2012-05-09 22:53:19 【问题描述】:

我想在 Excel 2010 中使用 vba 循环浏览目录的文件。

在循环中,我需要:

文件名和 文件格式化的日期。

如果文件夹不超过 50 个文件,我已经编写了以下代码,它可以正常工作,否则速度会非常慢(我需要它来处理包含 >10000 个文件的文件夹)。这段代码的唯一问题是查找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

问题已解决:

    我的问题已通过以下解决方案解决,该解决方案使用Dir 以特定方式(15000 个文件为 20 秒)并使用命令FileDateTime 检查时间戳。 考虑到 20 秒以下的另一个答案,将缩短到不到 1 秒。

【问题讨论】:

对于 VBA,您的初始时间似乎仍然很慢。你在使用 Application.ScreenUpdating=false 吗? 你好像不见了code Set MyObj = New FileSystemObject 我发现人们很快就将 FSO 称为“慢”,这很让人难过,但是没有人提到通过简单地使用早期绑定而不是针对Object 的后期绑定调用可以避免的性能损失。 【参考方案1】:

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

【讨论】:

太棒了。这只是将运行时间从 20 秒提高到 这可能是因为 Do while...loop 比 while...wend 更好。更多信息在这里***.com/questions/32728334/… 我认为没有那个改进水平(20 - xxx 次) - 我认为通配符会有所作为。 DIR() 似乎没有返回隐藏文件。 @hamish,您可以更改其参数以返回不同类型的文件(隐藏、系统等) - 请参阅 MS 文档:docs.microsoft.com/en-us/office/vba/language/reference/…【参考方案2】:

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

【讨论】:

太好了,非常感谢。我确实使用 Dir,但我不知道您也可以这样使用它。除了命令FileDateTime,我的问题也解决了。 还有一个问题。如果 DIR 从最近的文件开始循环,我可以大大提高速度。你觉得有什么办法吗? 我的后一个问题已由 brettdj 下面的评论解决。 目录将not 但是traverse the whole directory tree。如有需要:analystcave.com/vba-dir-function-how-to-traverse-directories/… Dir 也会被其他 Dir 命令打断,所以如果你运行一个包含 Dir 的子程序,它可以在你原来的子程序中“重置”它。根据原始问题使用 FSO 可以消除此问题。编辑:刚刚看到下面@LimaNightHawk 的帖子,同样的事情【参考方案3】:

这是我作为函数的解释:

'#######################################################################
'# LoopThroughFiles
'# Function to Loop through files in current directory and return filenames
'# Usage: LoopThroughFiles ActiveWorkbook.Path, "txt" 'inputDirectoryToScanForFile
'# https://***.com/questions/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

【讨论】:

为什么函数,什么时候没有返回?这和 brettdj 给出的答案不一样,只是它包含在一个函数中【参考方案4】:

Dir 函数是可行的方法,但问题是您不能递归地使用Dir 函数,如here, towards the bottom 所述。

我处理这个问题的方法是使用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 GetFileList 方法返回一个字符串数组。您可能只是遍历数组并将项目添加到 ListView 或类似的东西。有关如何在列表视图中显示项目的详细信息可能超出了本文的范围。【参考方案5】:

Dir 函数在我处理和处理其他文件夹中的文件时很容易失去焦点。

我使用组件 FileSystemObject 获得了更好的结果。

这里给出了完整的例子:

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

不要忘记在 Visual Basic 编辑器中设置对 Microsoft 脚本运行时的引用(通过使用工具 > 引用)

试一试!

【讨论】:

从技术上讲,这是提问者使用的方法,他们只是没有包含他们的参考资料,这会减慢这种方法的速度。【参考方案6】:

试试这个。 (LINK)

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

【讨论】:

以上是关于使用VBA循环浏览文件夹中的文件?的主要内容,如果未能解决你的问题,请参考以下文章

Excel VBA - 使用 Dir() 的多个实例?

使用循环打开文件路径中的所有excel文件后,有没有办法通过vba创建工作簿变量来引用这些文件?

访问中的 Vba 代码循环遍历文件夹中的所有 excel 文件,打开、保存和关闭它们

如何让我的浏览对话框转到 VBA 中的特定文件夹?

Excel VBA - 循环遍历多个文件夹中的文件,复制范围,粘贴到此工作簿中

使用 VBA Excel 浏览文件夹以在 Outlook 邮件中附加文件 [重复]