Перебирать файлы в папке с помощью VBA?

236

Я хотел бы перебрать файлы каталога с помощью в Excel 2010

В цикле мне понадобятся:

  • имя файла и
  • дата, когда файл был отформатирован.

Я написал следующее, что работает нормально, если в папке не более 50 файлов, в противном случае это смехотворно медленно (мне нужно для работы с папками с> 10000 файлов). Единственная проблема этого кода в том, что операция поиска file.nameзанимает очень много времени.

Код, который работает, но работает слишком медленно (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 секунды.
TYREX
источник
Ваше начальное время кажется медленным для VBA все еще. Используете ли вы Application.ScreenUpdating = false?
Михель ван дер Блонк
2
Похоже, вы отсутствуете codeSet MyObj = New FileSystemObject
baldmosher
13
Мне довольно грустно, что люди быстро называют FSO «медленным», но никто не упоминает о снижении производительности, которого можно избежать, просто используя раннее связывание вместо вызовов с поздним связыванием Object.
Матье

Ответы:

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
benmichae2.
источник
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
brettdj
источник
29
ЗДОРОВО. Это только увеличило время выполнения с 20 секунд до <1 секунды. Это большое улучшение, так как код будет запускаться довольно часто. СПАСИБО!!
Tyrex
Это может быть потому, что цикл Do while ... лучше, чем while ... wend. Более подробная информация здесь stackoverflow.com/questions/32728334/…
Hila DG
6
Я не думаю, что по этому уровню улучшения (20 - ххх раз) - я думаю, что подстановочный знак имеет значение.
brettdj
DIR () не возвращает скрытые файлы.
Хэмиш
@ Hamish, вы можете изменить его аргумент, чтобы он возвращал файлы другого типа (скрытые, системные и т. д.) - см. документацию MS: docs.microsoft.com/en-us/office/vba/language/reference/…
Винсент,
158

Дир, кажется, очень быстро.

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
источник
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функцию рекурсивно , как указано здесь, в нижней части .

Способ, которым я это обработал, состоит в том, чтобы использовать 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
LimaNightHawk
источник
Если я хотел бы перечислить файлы, найденные в столбце, что может быть реализация этого?
Джехавиз
@jechaviz Метод GetFileList возвращает массив строк. Вы, вероятно, просто перебираете массив и добавляете элементы в ListView или что-то в этом роде. Подробности того, как отображать элементы в списке, вероятно, выходят за рамки этого поста.
LimaNightHawk
6

Dir функция легко теряет фокус при обработке и обработке файлов из других папок.

Я получил лучшие результаты с компонентом FileSystemObject.

Полный пример приведен здесь:

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

Не забудьте установить ссылку в редакторе Visual Basic на 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
Меэлис Тара
источник