Czy przeglądać pliki w folderze za pomocą VBA?

236

Chciałbym przeglądać pliki katalogu za pomocą w programie Excel 2010.

W pętli będę potrzebować:

  • nazwa pliku i
  • data sformatowania pliku.

Zakodowałem następujące, co działa dobrze, jeśli folder nie ma więcej niż 50 plików, w przeciwnym razie jest absurdalnie wolny (potrzebuję go do pracy z folderami z> 10000 plików). Jedynym problemem tego kodu jest to, że operacja wyszukiwania file.namezajmuje bardzo dużo czasu.

Kod, który działa, ale jest zbyt wolny (15 sekund na 100 plików):

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

Problem rozwiązany:

  1. Mój problem został rozwiązany przez poniższe rozwiązanie wykorzystujące Dirw określony sposób (20 sekund dla 15000 plików) i sprawdzanie znacznika czasu za pomocą polecenia FileDateTime.
  2. Biorąc pod uwagę inną odpowiedź poniżej 20 sekund, skraca się do mniej niż 1 sekundy.
tyrex
źródło
Twój początkowy czas wydaje się wciąż wolny dla VBA. Czy używasz Application.ScreenUpdating = false?
Michiel van der Blonk
2
codeWygląda na to, że brakuje Ci zestawu MyObj = New FileSystemObject
baldmosher 25.01.2017
13
Wydaje mi się dość smutne, że ludzie szybko nazywają FSO „powolnym”, ale nikt nie wspomina o ograniczeniu wydajności, którego można by uniknąć, po prostu stosując wczesne wiązanie zamiast późnych połączeń przeciwko Object.
Mathieu Guindon,

Odpowiedzi:

46

Oto moja interpretacja jako funkcji:

'#######################################################################
'# 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
benmichae2.
źródło
25
po co funkcjonować, skoro nic nie jest zwracane? Nie jest to taka sama jak odpowiedź udzieloną przez brettdj, oprócz tego, że jest zamknięty w funkcji
Shafeek
253

Dirbierze symbole wieloznaczne, dzięki czemu można zrobić dużą różnicę, dodając filtr z testgóry i unikając testowania każdego pliku

Sub LoopThroughFiles()
    Dim StrFile As String
    StrFile = Dir("c:\testfolder\*test*")
    Do While Len(StrFile) > 0
        Debug.Print StrFile
        StrFile = Dir
    Loop
End Sub
brettdj
źródło
29
ŚWIETNY. To tylko poprawiło czas działania z 20 sekund do <1 sekundy. To duża poprawa, ponieważ kod będzie uruchamiany dość często. DZIĘKUJĘ CI!!
tyrex
Może być tak, ponieważ pętla Do while ... jest lepsza niż podczas ... wend. więcej informacji tutaj stackoverflow.com/questions/32728334/…
Hila DG
6
Nie sądzę, że przez ten poziom ulepszenia (20 - xxx razy) - myślę, że to wieloznaczne robi różnicę.
brettdj
DIR () nie zwraca ukrytych plików.
hamish
@hamish, możesz zmienić jego argument, aby zwracał inny typ plików (ukryty, systemowy itp.) - patrz dokumentacja MS: docs.microsoft.com/en-us/office/vba/language/reference/…
Vincent
158

Dir wydaje się być bardzo szybki.

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
grantnz
źródło
3
Świetnie Dziękuję Ci bardzo. Używam Dir, ale nie wiedziałem, że możesz używać tego w ten sposób. Oprócz polecenia FileDateTimemój problem został rozwiązany.
tyrex
4
Wciąż jedno pytanie. Mógłbym poważnie poprawić prędkość, jeśli DIR zapętliby się, zaczynając od najnowszych plików. Czy widzisz jak to zrobić?
tyrex
3
Moje ostatnie pytanie zostało rozwiązane przez komentarz poniżej z brettdj.
tyrex
Dir notjednak traverse the whole directory tree. W razie potrzeby: analystcave.com/vba-dir-function-how-to-traverse-directories/…
AnalystCave.com
Dir zostanie również przerwany przez inne polecenia Dir, więc jeśli uruchomisz podprogram zawierający Dir, może on „zresetować” go w twoim oryginalnym sub. Używanie FSO zgodnie z pierwotnym pytaniem eliminuje ten problem. EDYCJA: właśnie widziałem post przez @LimaNightHawk poniżej, to samo
baldmosher
26

Funkcją Dir jest droga, ale problem polega na tym, że nie można użyć Dirfunkcji rekurencyjnej , jak stwierdzono tutaj, w dół .

Sposób, w jaki sobie z tym poradziłem, to użycie Dir funkcji, aby pobrać wszystkie podfoldery dla folderu docelowego i załadować je do tablicy, a następnie przekazać tablicę do funkcji, która się powtarza.

Oto klasa, którą napisałem, która to osiąga, obejmuje możliwość wyszukiwania filtrów. ( Będziesz musiał wybaczyć notację węgierską, która została napisana, gdy była cała furia ).

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
LimaNightHawk
źródło
Jeśli chciałbym wyświetlić listę plików znalezionych w kolumnie, jaka mogłaby to być implementacja?
jechaviz
@jechaviz Metoda GetFileList zwraca tablicę String. Prawdopodobnie po prostu wykonasz iterację po tablicy i dodasz elementy do ListView lub czegoś podobnego. Szczegóły dotyczące wyświetlania elementów w widoku listy prawdopodobnie wykraczają poza zakres tego postu.
LimaNightHawk
6

Dir funkcja łatwo traci ostrość, gdy przetwarzam i przetwarzam pliki z innych folderów.

Z komponentem uzyskałem lepsze wyniki FileSystemObject.

Pełny przykład podano tutaj:

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

Nie zapomnij ustawić odniesienia w edytorze Visual Basic do środowiska wykonawczego Microsoft Scripting Runtime (za pomocą Narzędzia> Referencje)

Spróbuj!

Felipe Gaviria Correa
źródło
Technicznie jest to metoda, z której korzysta pytający, po prostu nie ma dołączonych odniesień, co spowolniłoby tę metodę.
Marcucciboy2
-2

Spróbuj tego. ( 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
Meelis Tara
źródło