Excel VBA扫描目录内容并更新表

时间:2016-07-04 02:25:43

标签: excel vba excel-vba

我一直在制作一个vba脚本来扫描目录的内容并使用该扫描结果更新表格。结果是文件是Unchanged,New或Missing,它将更新到表的File Status列。如果它是新的,那么它会将文件名放入表格的Filename列中。

我的代码相当接近,但我遇到了一些通过两个数组的逻辑问题。我已经耗尽了我的能力而不会在代码中造成更多问题而进行查看,进一步的更改会导致代码中的回归。

任何人都可以查看代码,看看我是否在正确的道路上,或者我是否犯了一个简单的错误?它错误地标记为某些文件缺失或新标记不应该出现,但我相信它会在出现其他任何错误之前正确标记未更改的文件。

Sub FolderContents()

Dim objFSO, objFolder, objFile As Object
Dim g, h, i, j, k, l As Integer
Dim myTable As ListObject
Dim myArray As Variant
Dim FileArray(), FileStatusArray() As String
Dim wsName, tbName, fnName, fsName, Path As String
Dim colNumFile, colNumStatus As Long
Dim newRow As ListRow
h = 1
j = 1
l = 1

' Change only these values if name of table or worksheets change
wsName = "Signage List"     'Worksheet name that contains the signage table
tbName = "Signage"          'Table name for the signage file data
fnName = "Filename"         'Column name that contains the file names
fsName = "File Status"      'Column name that contains the file statuses

' ! DO NOT EDIT ANYTHING BELOW THIS LINE !

Application.FileDialog(msoFileDialogFolderPicker).ButtonName = "Select Folder"
Set objFSO = CreateObject("Scripting.FileSystemObject")

With Application.FileDialog(msoFileDialogFolderPicker)
    .AllowMultiSelect = False
    .Title = "Select destination folder"
    If .Show = -1 And .SelectedItems.Count = 1 Then
        Path = .SelectedItems(1)
    Else: Exit Sub
    End If
End With

Set objFolder = objFSO.GetFolder(Path)
Set myTable = Worksheets(wsName).ListObjects(tbName)
colNumFile = myTable.ListColumns(fnName).Index
colNumStatus = myTable.ListColumns(fsName).Index

If Not myTable.ListColumns(colNumFile).DataBodyRange Is Nothing Then
    myArray = myTable.ListColumns(colNumFile).DataBodyRange
End If

If Not IsEmpty(myArray) Then
    For Each objFile In objFolder.Files
        If objFile.Type = "PNG image" Then
            For i = LBound(myArray) To UBound(myArray)
                ReDim Preserve FileArray(1 To j)
                ReDim Preserve FileStatusArray(1 To j)
                If myArray(i, 1) = objFile.Name Then
                    FileArray(j) = objFile.Name
                    Cells(i + 1, colNumStatus) = "Unchanged"
                    FileStatusArray(j) = "Unchanged"
                    GoTo NextFile
                Else
                    FileArray(j) = objFile.Name
                    FileStatusArray(j) = "New"
                End If
            Next i
NextFile:
            j = j + 1
        End If
    Next objFile

    For k = LBound(FileArray) To UBound(FileArray)
        For l = LBound(myArray) To UBound(myArray)
            If Not myArray(l, 1) = FileArray(k) Then
                Cells(l + 1, colNumStatus) = "Missing"
                GoTo AnotherFile
            Else
                    Cells(l + 1, colNumStatus) = "Unchanged"
            End If
            Next l
AnotherFile:

        If FileStatusArray(k) = "New" Then
            Set newRow = myTable.ListRows.Add(AlwaysInsert:=True)
            Set myTable = Worksheets(wsName).ListObjects(tbName)
            newRow.Range.Cells(1, colNumStatus) = "New"
            newRow.Range.Cells(1, colNumFile) = FileArray(k)
        End If
    Next k
Else
    For Each objFile In objFolder.Files
        If objFile.Type = "PNG image" Then
            ReDim Preserve FileArray(1 To h)
            ReDim Preserve FileStatusArray(1 To h)
            FileArray(h) = objFile.Name
            FileStatusArray(h) = "New"
            h = h + 1
        End If
    Next objFile

    For g = LBound(FileArray) To UBound(FileArray)
        Set newRow = myTable.ListRows.Add(AlwaysInsert:=True)
        Set myTable = Worksheets(wsName).ListObjects(tbName)
        newRow.Range.Cells(1, colNumStatus) = "New"
        newRow.Range.Cells(1, colNumFile) = FileArray(g)
    Next g
End If

End Sub

谢谢!

1 个答案:

答案 0 :(得分:0)

我没有多次遍历范围或数组,而是将值添加到字典中。我也尝试将大型子程序分解为更小的任务。

Sub FolderContents()
    Application.ScreenUpdating = False
    ' Change only these values if name of table or worksheets change
    Const wsName = "Signage List"    'Worksheet name that contains the signage table
    Const tbName = "Signage"      'Table name for the signage file data
    Const fnName = "Filename"     'Column name that contains the file names
    Const fsName = "File Status"  'Column name that contains the file statuses
    Dim dImageFiles
    Dim tblSignage As ListObject
    Dim newRow As Range
    Dim k As String

    Dim x As Long, colNumFile As Long, colNumStatus As Long

    Set dImageFiles = getSignageImageFilesDictionary

    If dImageFiles.Count = 0 Then
        ' Do Something if no folder was selected
    End If

    Set tblSignage = Worksheets(wsName).ListObjects(tbName)

    With tblSignage
        colNumFile = .ListColumns(fnName).Index
        colNumStatus = .ListColumns(fsName).Index
        With .DataBodyRange
            For x = 1 To .Rows.Count
                k = .Cells(x, colNumFile).Text
                If dImageFiles.Exists(k) Then
                    .Cells(x, colNumStatus) = "Unchanged"
                    dImageFiles.Remove k
                Else
                    .Cells(x, colNumStatus) = "Missing"
                End If

            Next x

        End With
    End With

    For x = 0 To dImageFiles.Count - 1
        Set newRow = tblSignage.ListRows.Add(AlwaysInsert:=True).Range
        newRow.Cells(1, colNumFile) = dImageFiles.keys(x)
        newRow.Cells(1, colNumStatus) = "New"
    Next x

    Application.ScreenUpdating = True
End Sub

Function getSignageImageFilesDictionary()
    Dim folderPath As String
    Dim dict, fso, f

    Set dict = CreateObject("Scripting.Dictionary")
    Set fso = CreateObject("Scripting.FileSystemObject")

    folderPath = getFolderPath
    If Len(folderPath) Then
        For Each f In fso.GetFolder(folderPath).Files

            If fso.GetExtensionName(f.Path) = "png" Then
                If Not dict.Exists(f.Name) Then dict.Add f.Name, f.Path
            End If

        Next
    End If

    Set getSignageImageFilesDictionary = dict
    Set fso = Nothing
End Function

Function getFolderPath() As String

    Application.FileDialog(msoFileDialogFolderPicker).ButtonName = "Select Folder"

    With Application.FileDialog(msoFileDialogFolderPicker)
        .AllowMultiSelect = False
        .Title = "Select destination folder"
        If .Show = -1 And .SelectedItems.Count = 1 Then
            getFolderPath = .SelectedItems(1)
        Else: Exit Function
        End If
    End With

End Function

我没有看到任何使用数组的理由。如果你正在处理大量的数据,我会通过转换表格的数据格式来创建一个多维数组。它需要转置,因为只能调整数组的最后一个维度。接下来可以是数组的值并向其添加新行。最后,我们将数组转换为existiong数据集范围。

Sub FolderContents()
    ' Change only these values if name of table or worksheets change
    Const wsName = "Signage List"        'Worksheet name that contains the signage table
    Const tbName = "Signage"          'Table name for the signage file data
    Const fnName = "Filename"         'Column name that contains the file names
    Const fsName = "File Status"      'Column name that contains the file statuses
    Dim dImageFiles
    Dim tblSignage As ListObject
    Dim k As String
    Dim x As Long, count As Long, colCount As Long, colNumFile As Long, colNumStatus As Long
    Dim arData, v

    Set dImageFiles = getSignageImageFilesDictionary

    If dImageFiles.count = 0 Then
        ' Do Something if no folder was selected
    End If

    Set tblSignage = Worksheets(wsName).ListObjects(tbName)

    With tblSignage
        colNumFile = .ListColumns(fnName).Index
        colNumStatus = .ListColumns(fsName).Index
        colCount = .DataBodyRange.Columns.count

        arData = WorksheetFunction.Transpose(.DataBodyRange)

        For x = 1 To UBound(arData, 2)

            k = arData(colNumFile, x)
            If dImageFiles.Exists(k) Then
                arData(colNumStatus, x) = "Unchanged"
                dImageFiles.Remove k
            Else
                arData(colNumStatus, x) = "Missing"
            End If
        Next x

        For Each v In dImageFiles.keys()
            count = UBound(arData, 2) + 1
            ReDim Preserve arData(1 To colCount, 1 To count)
            arData(colNumFile, count) = v
            arData(colNumStatus, count) = "New"
        Next v

        .DataBodyRange.Cells(1, 1).Resize(UBound(arData, 2), colCount) = WorksheetFunction.Transpose(arData)

    End With

End Sub