วนไฟล์ในโฟลเดอร์โดยใช้ VBA หรือไม่


236

ฉันต้องการวนไฟล์ของไดเรกทอรีโดยใช้ ใน Excel 2010

ในลูปฉันจะต้อง:

  • ชื่อไฟล์และ
  • วันที่ซึ่งฟอร์แมตไฟล์

ฉันเขียนโค้ดต่อไปนี้ซึ่งใช้งานได้ดีหากโฟลเดอร์ไม่มีไฟล์มากกว่า 50 ไฟล์มิฉะนั้นจะช้าอย่างน่าขัน (ฉันต้องใช้เพื่อทำงานกับโฟลเดอร์ที่มีไฟล์> 10,000 ไฟล์) ปัญหาเดียวของรหัสนี้คือการดำเนินการเพื่อค้นหาfile.nameใช้เวลานานมาก

รหัสที่ใช้งานได้ แต่ waaaaaay ช้าเกินไป (15 วินาทีต่อ 100 ไฟล์):

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ในทางหนึ่ง (20 วินาทีสำหรับ 15000 ไฟล์) FileDateTimeและสำหรับการตรวจสอบการประทับเวลาใช้คำสั่ง
  2. คำนึงถึงคำตอบอื่นจากด้านล่าง 20 วินาทีจะลดลงเหลือน้อยกว่า 1 วินาที

เวลาเริ่มต้นของคุณดูเหมือนช้าสำหรับ VBA ยัง คุณใช้ Application.ScreenUpdating = false หรือไม่
Michiel van der Blonk

2
ดูเหมือนว่าคุณจะขาดcodeชุด MyObj = ใหม่ FileSystemObject
baldmosher

13
ฉันคิดว่ามันค่อนข้างน่าเศร้าที่ผู้คนมีความรวดเร็วในการเรียก FSO "ช้า" Objectแต่ไม่มีใครกล่าวถึงโทษประสิทธิภาพที่คุณสามารถหลีกเลี่ยงโดยเพียงแค่ใช้ต้นผูกพันแทนของสายปลายที่ถูกผูกไว้กับ
Mathieu Guindon

คำตอบ:


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
GREAT นี่เพิ่งปรับปรุงรันไทม์จาก 20 วินาทีเป็น <1 วินาที นั่นเป็นการปรับปรุงครั้งใหญ่เนื่องจากโค้ดจะทำงานได้ค่อนข้างบ่อย ขอบคุณ!!
tyrex

อาจเป็นเพราะห่วง Do Do ... ห่วงนั้นดีกว่าในขณะที่ ... wend ข้อมูลเพิ่มเติมที่นี่stackoverflow.com/questions/32728334/…
Hila DG

6
ฉันไม่คิดว่าจะมีการปรับปรุงในระดับนั้น (20 - xxx ครั้ง) - ฉันคิดว่ามันเป็นตัวแทน
brettdj

DIR () ดูเหมือนจะไม่ส่งคืนไฟล์ที่ซ่อนอยู่
hamish

@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

4
ยังคงเป็นหนึ่งคำถาม ฉันสามารถปรับปรุงความเร็วอย่างรุนแรงหาก DIR จะวนซ้ำเริ่มต้นด้วยไฟล์ล่าสุด คุณเห็นวิธีการทำเช่นนี้หรือไม่?
tyrex

3
คำถามหลังของฉันได้รับการตัดสินโดยความคิดเห็นด้านล่างจาก brettdj
tyrex

ผบ. จะอย่างไรก็ตามnot traverse the whole directory treeในกรณีที่จำเป็น: analystcave.com/vba-dir-function-how-to-traverse-directories/ …
AnalystCave.com

Dir จะถูกขัดจังหวะด้วยคำสั่ง Dir อื่น ๆ ดังนั้นหากคุณเรียกใช้รูทีนย่อยที่มี Dir อยู่มันสามารถ "รีเซ็ต" มันในส่วนย่อยดั้งเดิมของคุณ การใช้ FSO ตามคำถามเดิมช่วยขจัดปัญหานี้ แก้ไข: เพิ่งเห็นโพสต์โดย @LimaNightHawk ด้านล่างสิ่งเดียวกัน
baldmosher

26

ฟังก์ชั่นผบ. เป็นวิธีที่จะไป แต่ปัญหาคือการที่คุณไม่สามารถใช้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

@jechaviz วิธี GetFileList ส่งคืนอาร์เรย์ของ String คุณอาจจะทำซ้ำมากกว่าอาร์เรย์และเพิ่มรายการลงใน ListView หรืออะไรทำนองนั้น รายละเอียดเกี่ยวกับวิธีแสดงรายการในลิสต์วิวอาจอยู่นอกเหนือขอบเขตของโพสต์นี้
LimaNightHawk

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.