【问题标题】:Renaming Files in directory not only a folder重命名目录中的文件不仅是文件夹
【发布时间】:2021-02-26 18:21:48
【问题描述】:

我正在用 excel 处理一个项目,我正在重命名多个文件。

现在我正在使用此代码

Sub RenameFiles()  

Dim xDir As String  
Dim xFile As String  
Dim xRow As Long  
With Application.FileDialog(msoFileDialogFolderPicker)  
    .AllowMultiSelect = False  
If .Show = -1 Then  
    xDir = .SelectedItems(1)  
    xFile = Dir(xDir & Application.PathSeparator & "*")  
    Do Until xFile = ""  
        xRow = 0  
        On Error Resume Next  
        xRow = Application.Match(xFile, Range("A:A"), 0)  
        If xRow > 0 Then  
            Name xDir & Application.PathSeparator & xFile As _  
            xDir & Application.PathSeparator & Cells(xRow, "G").Value  
        End If  
        xFile = Dir  
    Loop  
End If  
End With    
End Sub    

这让我可以更改一个特定文件夹中文件的名称,但我希望能够选择包含子文件夹的主文件夹,它会更改与我在我的 excel 表中创建的名称相对应的所有名称。

【问题讨论】:

  • 所以,你是说上面的代码是有效的,你希望我们给你一个代码来做你在帖子末尾写的东西:我希望能够选择包含子文件夹的主文件夹,它会更改与我在 Excel 表中创建的名称相对应的所有名称。
  • 是的,代码有效,但我只能选择一个包含文件的文件夹。
  • 我希望它通过整个文件夹,女巫有一个子文件夹,这样我可以更改更多文件,而不仅仅是直接在主文件夹中的文件
  • 这能回答你的问题吗? Loop Through All Subfolders Using VBA

标签: excel vba file directory rename


【解决方案1】:

我相信您知道重命名文件如果出错可能会产生非常严重的后果,有时甚至是灾难性的后果,话虽如此,我希望已采取所有必要措施来避免这些问题。

数据和代码:
AG 列似乎包含文件的 "old""new" 名称(不包括路径),这就是询问的原因路径的用户以及为子文件夹运行文件重命名的可能性。

发布的代码将文件夹(以及预期的子文件夹)中的每个文件与数据中的文件列表进行比较,这可能很耗时。

另外,我建议跟踪哪些文件已被重命名,以便在出现任何错误时,可以轻松地追溯并撤消可能出现的错误。

提出的解决方案
下面提出的解决方案使用FileSystemObject object,它提供了对机器文件系统的强大访问,您可以通过两种方式与之交互:Early and Late Binding (Visual Basic)。这些程序使用后期绑定,要使用早期绑定见How do I use FileSystemObject in VBA?

  1. Folders_ƒGet_From_User:要求用户选择文件夹并处理或不处理子文件夹的函数。它返回所选子文件夹的列表(仅名称),不包括没有文件的文件夹。
  2. Files_Get_Array:使用所有要处理的文件名创建和排列(旧的和新的)
  3. Files_ƒRename:此函数重命名从第 1 点获得的列表中的任何文件夹中找到的所有文件。这些过程不是根据列表验证子文件夹中存在的每个文件,而是检查列表中的文件Exist 在任何文件夹中,如果是,则传递给执行重命名并返回结果的函数 File_ƒRename_Apply,从而允许创建“Audit Track”数组。它返回一个数组,其中包含所有文件夹列表中列表中所有文件名的结果(从第 1 点和第 2 点开始)。
  4. File_Rename_Post_Records:创建一个名为 FileRename(Track) 的工作表(如果不存在)以发布 Files_ƒRename 函数结果的 Audit Track

它们都是从过程中调用的:Files_Rename

如果您对所使用的资源有任何疑问,请告诉我。

Option Explicit

Private Const mk_Wsh As String = "FileRename(Track)"
Private Const mk_MsgTtl As String = "Files Rename"
Private mo_Fso As Object

Sub Files_Rename()
    Dim aFolders() As String, aFiles As Variant
    Dim aRenamed As Variant

    
    Set mo_Fso = CreateObject("Scripting.FileSystemObject")
    
    If Not (Folders_ƒGet_From_User(aFolders)) Then Exit Sub
                    
    Call Files_Get_Array(aFiles)
                    
    If Not (Files_ƒRename(aRenamed, aFolders, aFiles)) Then
        Call MsgBox("None file was renamed", vbInformation, mk_MsgTtl)
        Exit Sub
    End If
   
    Call File_Rename_Post_Records(aFiles, aRenamed)
    Call MsgBox("Files were renamed" & String(2, vbLf) _
        & vbTab & "see details in sheet [" & mk_Wsh & "]", vbInformation, mk_MsgTtl)
                   
    End Sub

Private Function Folders_ƒGet_From_User(aFolders As Variant) As Boolean
Dim aFdrs As Variant
Dim oFdr As Object, sFolder As String, blSubFdrs As Boolean
    
    Erase aFolders
    
    With Application.FileDialog(msoFileDialogFolderPicker)
        .AllowMultiSelect = False
        If .Show <> -1 Then Exit Function
        sFolder = .SelectedItems(1)
    End With
    
    If MsgBox("Do you want to include subfolders?", _
        vbQuestion + vbYesNo + vbDefaultButton2, _
            mk_MsgTtl) = vbYes Then blSubFdrs = True

    Set oFdr = mo_Fso.GetFolder(sFolder)
    
    Select Case blSubFdrs
    
    Case False
        
        If oFdr.Files.Count > 0 Then
            aFdrs = aFdrs & "|" & oFdr.Path
        
        Else
            MsgBox "No files found in folder:" & String(2, vbLf) & _
                        vbTab & sFolder & String(2, vbLf) & _
                            vbTab & "Process is being terminated.", _
                                vbInformation, mk_MsgTtl
            Exit Function
        
        End If
        
    Case Else
        
        Call SubFolders_Get_Array(aFdrs, oFdr)

        If aFdrs = vbNullString Then
            MsgBox "No files found in folder & subfolders:" & String(2, vbLf) & _
                        vbTab & sFolder & String(2, vbLf) & _
                            vbTab & "Process is being terminated.", _
                                vbInformation, mk_MsgTtl
            Exit Function
    
        End If
        
    End Select
    
    Rem String To Array
    aFdrs = Mid(aFdrs, 2)
    aFdrs = Split(aFdrs, "|")
    aFolders = aFdrs
    
    Folders_ƒGet_From_User = True
    
    End Function

Private Sub SubFolders_Get_Array(aFdrs As Variant, oFdr As Object)
Dim oSfd As Object
    
    With oFdr
        If .Files.Count > 0 Then aFdrs = aFdrs & "|" & .Path
        For Each oSfd In .SubFolders
            Call SubFolders_Get_Array(aFdrs, oSfd)
    Next: End With
    
    End Sub

Private Sub Files_Get_Array(aFiles As Variant)
Dim lRow As Long
    
    With ThisWorkbook.Sheets("DATA")  'change as required
        lRow = .Rows.Count
        If Len(.Cells(lRow, 1).Value) = 0 Then lRow = .Cells(lRow, 1).End(xlUp).Row
        aFiles = .Cells(2, 1).Resize(-1 + lRow, 7).Value
    End With

    End Sub

Private Function Files_ƒRename(aRenamed As Variant, aFolders As Variant, aFiles As Variant) As Boolean
Dim vRcd As Variant:    vRcd = Array("Filename.Old", "Filename.New")
Dim blRenamed As Boolean
Dim oDtn As Object, aRcd() As String, lRow As Long, bFdr As Byte
Dim sNameOld As String, sNameNew As String
Dim sFilename As String, sResult As String
    
    aRenamed = vbNullString
    
    Set oDtn = CreateObject("Scripting.Dictionary")
    vRcd = Join(vRcd, "|") & "|" & Join(aFolders, "|")
    vRcd = Split(vRcd, "|")
    oDtn.Add 0, vRcd
                    
    With mo_Fso
        
        For lRow = 1 To UBound(aFiles)

            sNameOld = aFiles(lRow, 1)
            sNameNew = aFiles(lRow, 7)
            vRcd = sNameOld & "|" & sNameNew
            
            For bFdr = 0 To UBound(aFolders)
            
                sResult = Chr(39)
                sFilename = .BuildPath(aFolders(bFdr), sNameOld)
                            
                If .FileExists(sFilename) Then
    
                    If File_ƒRename_Apply(sResult, sNameNew, sFilename) Then blRenamed = True
            
                End If
            
                vRcd = vRcd & "|" & sResult
            
            Next
           
            vRcd = Mid(vRcd, 2)
            vRcd = Split(vRcd, "|")
            oDtn.Add lRow, vRcd
    
    Next: End With
    
    If Not (blRenamed) Then Exit Function
    
    aRenamed = oDtn.Items
    aRenamed = WorksheetFunction.Index(aRenamed, 0, 0)
    Files_ƒRename = True
    
    End Function

Private Function File_ƒRename_Apply(sResult As String, sNameNew As String, sFileOld As String) As Boolean
    
    With mo_Fso.GetFile(sFileOld)
        
        sResult = .ParentFolder
        On Error Resume Next
        .Name = sNameNew
        If Err.Number <> 0 Then
            sResult = "¡Err: " & Err.Number & " - " & Err.Description
            Exit Function
        End If
        On Error GoTo 0
    
    End With
            
    File_ƒRename_Apply = True
    
    End Function

Private Sub File_Rename_Post_Records(aFiles As Variant, aRenamed As Variant)
Const kLob As String = "lo.Audit"
Dim blWshNew As Boolean
Dim Wsh As Worksheet, Lob As ListObject, lRow As Long
    
    Rem Worksheet Set\Add
    With ThisWorkbook
        
        On Error Resume Next
        Set Wsh = .Sheets(mk_Wsh)
        On Error GoTo 0
        
        If Wsh Is Nothing Then
            
            .Worksheets.Add After:=.Sheets(.Sheets.Count)
            Set Wsh = .Sheets(.Sheets.Count)
            blWshNew = True
        
    End If: End With
        
    Rem Set ListObject
    With Wsh
        
        .Name = mk_Wsh
        .Activate
        Application.GoTo .Cells(1), 1
        
        Select Case blWshNew
        
        Case False
            
            Set Lob = .ListObjects(kLob)
            lRow = 1 + Lob.ListRows.Count

        Case Else

            With .Cells(2, 2).Resize(1, 4)
                .Value = Array("TimeStamp", "Filename.Old", "Filename.New", "Folder.01")
                Set Lob = .Worksheet.ListObjects.Add(xlSrcRange, .Resize(2), , xlYes)
                Lob.Name = "lo.Audit"
                lRow = 1
            
    End With: End Select: End With
        
    Rem Post Data
    With Lob.DataBodyRange.Cells(lRow, 1).Resize(UBound(aRenamed), 1)
        .Value = Format(Now, "YYYYMMDD_HHMMSS")
        .Offset(0, 1).Resize(, UBound(aRenamed, 2)).Value = aRenamed
        .CurrentRegion.Columns.AutoFit
    End With
    
    End Sub

【讨论】:

    【解决方案2】:

    重命名文件(子文件夹)

    • 几乎没有经过足够的测试。
    • 您最好在应该运行它的文件夹中创建一个副本,以避免丢失文件。
    • 它将文件夹及其子文件夹中的所有文件写入字典对象,其键(文件路径)将根据列A 中的文件路径进行检查。如果匹配,文件将重命名为 G 列中的名称,文件路径相同。
    • 在重命名之前,它仅根据字典中的文件路径检查每个新文件路径。
    • 如果文件名无效,它将失败。
    • 将完整代码复制到标准模块,例如Module1
    • 调整第一个过程的常量部分中的值。
    • 只运行第一个过程,其余的由它调用。

    守则

    Option Explicit
    
    Sub renameFiles()
        
        ' Define constants.
        Const wsName As String = "Sheet1"
        Const FirstRow As Long = 2
        Dim Cols As Variant
        Cols = Array("A", "G")
        Dim wb As Workbook
        Set wb = ThisWorkbook
        
        ' Define worksheet.
        Dim ws As Worksheet
        Set ws = wb.Worksheets(wsName)
        ' Define Lookup Column Range.
        Dim rng As Range
        Set rng = defineColumnRange(ws, Cols(LBound(Cols)), FirstRow)
        ' Write values from Column Ranges to jagged Column Ranges Array.
        Dim ColumnRanges As Variant
        ColumnRanges = getColumnRanges(rng, Cols)
        
        ' Pick a folder.
        Dim FolderPath As String
        FolderPath = pickFolder
        
        ' Define a Dictionary object.
        Dim dict As Object
        Set dict = CreateObject("Scripting.Dictionary")
        ' Write the paths and the names of the files in the folder
        ' and its subfolders to the Dictionary.
        Set dict = getFilesDictionary(FolderPath)
        
        ' Rename files.
        Dim RenamesCount As Long
        RenamesCount = renameColRngDict(ColumnRanges, dict)
    
        ' Inform user.
        If RenamesCount > 0 Then
            MsgBox "Renamed " & RenamesCount & " file(s).", vbInformation, "Success"
        Else
            MsgBox "No files renamed.", vbExclamation, "No Renames"
        End If
        
    End Sub
    
    Function defineColumnRange(Sheet As Worksheet, _
                               ColumnIndex As Variant, _
                               FirstRowNumber As Long) _
      As Range
        Dim rng As Range
        Set rng = Sheet.Cells(FirstRowNumber, ColumnIndex) _
                       .Resize(Sheet.Rows.Count - FirstRowNumber + 1)
        Dim cel As Range
        Set cel = rng.Find(What:="*", _
                           LookIn:=xlFormulas, _
                           SearchDirection:=xlPrevious)
        If Not cel Is Nothing Then
            Set defineColumnRange = rng.Resize(cel.Row - FirstRowNumber + 1)
        End If
    End Function
    
    Function getColumnRanges(ColumnRange As Range, _
                             BuildColumns As Variant) _
      As Variant
        Dim Data As Variant
        ReDim Data(LBound(BuildColumns) To UBound(BuildColumns))
        Dim j As Long
        With ColumnRange.Columns(1)
            For j = LBound(BuildColumns) To UBound(BuildColumns)
                If .Rows.Count > 1 Then
                    Data(j) = .Offset(, .Worksheet.Columns(BuildColumns(j)) _
                      .Column - .Column).Value
                Else
                    Dim OneCell As Variant
                    ReDim OneCell(1 To 1, 1 To 1)
                    Data(j) = OneCell
                    Data(1, 1) = .Offset(, .Worksheet.Columns(BuildColumns(j)) _
                      .Column - .Column).Value
                End If
            Next j
        End With
        getColumnRanges = Data
    End Function
    
    Function pickFolder() _
      As String
        With Application.FileDialog(msoFileDialogFolderPicker)
            .AllowMultiSelect = False
            If .Show = -1 Then
                pickFolder = .SelectedItems(1)
            End If
        End With
    End Function
    
    ' This cannot run without the 'listFiles' procedure.
    Function getFilesDictionary(ByVal FolderPath As String) _
      As Object
        Dim dict As Object ' ByRef
        Set dict = CreateObject("Scripting.Dictionary")
        With CreateObject("Scripting.FileSystemObject")
            listFiles dict, .GetFolder(FolderPath)
        End With
        Set getFilesDictionary = dict
    End Function
    
    ' This is being called only by 'getFileDictionary'
    Sub listFiles(ByRef Dictionary As Object, _
                  fsoFolder As Object)
        Dim fsoSubFolder As Object
        Dim fsoFile As Object
        For Each fsoFile In fsoFolder.Files
            Dictionary(fsoFile.Path) = Empty 'fsoFile.Name
        Next fsoFile
        For Each fsoSubFolder In fsoFolder.SubFolders
            listFiles Dictionary, fsoSubFolder
        Next
    End Sub
    
    ' Breaking the rules:
    ' A Sub written as a function to return the number of renamed files.
    Function renameColRngDict(ColumnRanges As Variant, _
                              Dictionary As Object) _
      As Long
        Dim Key As Variant
        Dim CurrentIndex As Variant
        Dim NewFilePath As String
        For Each Key In Dictionary.Keys
            Debug.Print Key
            CurrentIndex = Application.Match(Key, _
              ColumnRanges(LBound(ColumnRanges)), 0)
            If Not IsError(CurrentIndex) Then
                NewFilePath = Left(Key, InStrRev(Key, Application.PathSeparator)) _
                  & ColumnRanges(UBound(ColumnRanges))(CurrentIndex, 1)
                If IsError(Application.Match(NewFilePath, Dictionary.Keys, 0)) Then
                    renameColRngDict = renameColRngDict + 1
                    Name Key As NewFilePath
                End If
            End If
        Next Key
    End Function
    

    【讨论】:

    • Thx,这太棒了,我只是遇到了一个问题,它说“没有重命名文件”,当我按下文件夹并按下 OK 时,我已将值“sheet1”更改为相应的工作表,但我还需要改变什么?
    • 我建议您创建应该发生这种情况的文件夹的副本,然后打开工作簿的副本并调整 A 列中文件名中的新路径。据我了解,G 列中只有文件名,而不是路径,因为路径将取自A 列中的文件名。如果不是这样,请澄清。当然,在更改工作表名称的正下方调整数据开始的列和第一行。如果消息“没有重命名文件”。弹出,这意味着没有找到文件(可能已经重命名了?)。
    • 所以我在 A 列中有我的原始名称,我想将其更改为 G 列。所以我想我可以在单击运行并选择文件夹时选择路径。还是我误解了什么。我添加了一个屏幕截图,希望对您有所帮助,或者我是否需要更改某些内容,或者我是否应该在名称中包含文件类型。 prntscr.com/vkbw1b 或者如果它有助于发送文件或其他东西。
    • 我预计例如C:\Test\File1.xlsmA 列中,New.xlsmG 列中。如果您将 Empty 'fsoFile.Name 更改为 fsoFile.Name 并将 Match(Key 更改为 Match(Dictionary(Key) ,也许它可以工作,例如File1.xlsm 在列 ANew.xlsm 在列 G。但我不会推荐它。我建议您按照我的预期将完整路径放入列A
    • 是的,当我输入a列的路径时,它现在可以正常工作了:) 非常感谢,这将为我节省很多时间:)
    猜你喜欢
    • 2014-07-12
    • 2015-10-26
    • 2012-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-08
    • 1970-01-01
    • 2019-02-07
    相关资源
    最近更新 更多