繁体   English   中英

Append 单个(和多个)工作表分布在文件夹中的多个 excel 工作表上

[英]Append individual (and multiple) sheets spread over multiple excel worksheets in a folder

我有一个包含 19 个 Excel 工作表的文件夹,其中每个工作表包含 16 个工作表。 虽然 excel 文件的名称不同,但其中各个工作表的名称是相同的,即它们从“工作表 1”和 go 开始直到“工作表 16”。 我要做的是 append 每张纸在下一张的末尾并继续相同以获得一个新的 excel 文件,其中仅包含一张纸。 (换句话说,文件二(例如,B)中的表 1 将附加在文件一(例如,A)中的表 1 的末尾,文件三(例如,C)中的表 1 将附加在上述两个文件分别包含文件 A 和 B 中的工作表 1,依此类推,直到添加每个文件)。

我试过 VBA 代码来提取每个工作表,然后重命名每个工作表,将它们转换为 CSV 然后组合它们。 但是这个过程需要太多时间,并且有很多这样的文件夹。 如果我能获得 VBA 代码、python 代码或 R 代码来自动完成所有这些操作,我将不胜感激(其中任何一个都可以。解释和感谢,如果有的话,提前致谢) .

我想我知道你在追求什么。 如果是这样,我已经根据您的要求改编了一些针对类似问题编写的代码。

打开这些文件夹中的第一个并在其中创建一个启用宏的工作簿。 工作簿的名称无关紧要。

在该工作簿中,创建一个名为“工作簿”的工作表。

切换到 Visual Basic 编辑器。 我喜欢将我的代码分布在几个模块上,每个模块都有一个模块。 如果您更愿意提供只有一个Option Explicit语句,则可以将以下所有代码包含在一个模块中。

创建一个模块并将其命名为“LibExcel”。 以下代码包含我的 Excel 特定例程库中的一般例程:

Option Explicit
Public Sub FindLastRowCol(ByRef Wsht As Worksheet, ByRef RowLast As Long, _
                          ByRef ColLast As Long)

  ' Sets RowLast to the last row and ColLast to the last column with a value
  ' in worksheet Wsht.  Cells(RowLast, ColLast) need not contain a value.
  ' That is, the data in Wsht does not have to be rectangular.

  ' The motivation for coding this routine was the discovery that Find by
  ' previous row found a cell formatted as Merge and Center but Find by
  ' previous column did not. I had known that Find would miss merged
  ' cells but this was new to me.

  '   Dec16  Coded
  ' 31Dec16  Corrected handling of UserRange
  ' 15Feb17  SpecialCells was giving a higher row number than Find for
  '          no reason I could determine.  Added code to check for a
  '          value on rows and columns above those returned by Find
  ' 25Jun17  Found column with value above that found by Find. Fixed.

  Dim ColCrnt As Long
  Dim ColLastFind As Long
  Dim ColLastOther As Long
  Dim ColLastTemp As Long
  Dim ColLeft As Long
  Dim ColRight As Long
  Dim Rng As Range
  Dim RowIncludesMerged As Boolean
  Dim RowBot As Long
  Dim RowCrnt As Long
  Dim RowLastFind As Long
  Dim RowLastOther As Long
  Dim RowLastTemp As Long
  Dim RowTop As Long

  With Wsht

    Set Rng = .Cells.Find("*", .Range("A1"), xlFormulas, , xlByRows, xlPrevious)
    If Rng Is Nothing Then
      RowLastFind = 0
      ColLastFind = 0
    Else
      RowLastFind = Rng.Row
      ColLastFind = Rng.Column
    End If

    Set Rng = .Cells.Find("*", .Range("A1"), xlValues, , xlByColumns, xlPrevious)
    If Rng Is Nothing Then
    Else
      If RowLastFind < Rng.Row Then
        RowLastFind = Rng.Row
      End If
      If ColLastFind < Rng.Column Then
        ColLastFind = Rng.Column
      End If
    End If

    Set Rng = .Range("A1").SpecialCells(xlCellTypeLastCell)
    If Rng Is Nothing Then
      RowLastOther = 0
      ColLastOther = 0
    Else
      RowLastOther = Rng.Row
      ColLastOther = Rng.Column
    End If

    Set Rng = .UsedRange
    If Rng Is Nothing Then
    Else
      If RowLastOther < Rng.Row + Rng.Rows.Count - 1 Then
        RowLastOther = Rng.Row + Rng.Rows.Count - 1
      End If
      If ColLastOther < Rng.Column + Rng.Columns.Count - 1 Then
        ColLastOther = Rng.Column + Rng.Columns.Count - 1
      End If
    End If

    If RowLastFind < RowLastOther Then
      ' Higher row found by SpecialCells or UserRange
      Do While RowLastOther > RowLastFind
        ColLastTemp = .Cells(RowLastOther, .Columns.Count).End(xlToLeft).Column
        If ColLastTemp > 1 Or .Cells(RowLastOther, 1).Value <> "" Then
          Debug.Assert False
          ' Is this possible
          ' Row after RowLastFind has value
          RowLastFind = RowLastOther
          Exit Do
        End If
        RowLastOther = RowLastOther - 1
      Loop
    ElseIf RowLastFind > RowLastOther Then
      Debug.Assert False
      ' Is this possible
    End If
    RowLast = RowLastFind

    If ColLastFind < ColLastOther Then
      ' Higher column found by SpecialCells or UserRange
      Do While ColLastOther > ColLastFind
        RowLastTemp = .Cells(.Rows.Count, ColLastOther).End(xlUp).Row
        If RowLastTemp > 1 Or .Cells(1, ColLastOther).Value <> "" Then
          'Debug.Assert False
          ' Column after ColLastFind has value
          ' Possible causes:
          '   * Find does not recognise merged cells
          '   ' Find does not examine hidden cells
          ColLastFind = ColLastOther
          Exit Do
        End If
        ColLastOther = ColLastOther - 1
      Loop
    ElseIf ColLastFind > ColLastOther Then
      Debug.Assert False
      ' Is this possible
    End If
    ColLast = ColLastFind

  End With

End Sub
Public Function WshtExists(ByRef Wbk As Workbook, ByVal WshtName As String)

  ' Returns True if Worksheet WshtName exists within
  '  * if Wbk Is Nothing the workbook containing the macros
  '  * else workbook Wbk

  ' 21Aug16  Coded by Tony Dallimore
  ' 14Feb17  Coded alternative routine that cycled through the existing worksheets
  '          matching their names against WshtName to check use of "On Error Resume Next"
  '          was the faster option. I needed to call the routines 6,000,000 times each to
  '          get an adequate duration for comparison. This version took 33 seconds while
  '          the alternative took 75 seconds.

  Dim WbkLocal As Workbook
  Dim Wsht As Worksheet

  If Wbk Is Nothing Then
    Set WbkLocal = ThisWorkbook
  Else
    Set WbkLocal = Wbk
  End If

  Err.Clear
  On Error Resume Next
  Set Wsht = WbkLocal.Worksheets(WshtName)
  On Error GoTo 0
  If Wsht Is Nothing Then
    WshtExists = False
  Else
    WshtExists = True
  End If

End Function

此代码属于“ModPrepare”模块。 注意“Mod”很重要。 该模块包含一个宏“准备”。 如果你给一个模块和一个宏赋予相同的名称,你会得到模糊的错误。

Option Explicit
Sub Prepare()

  ' Create list of workbooks (other than this workbook) in current folder

  Dim Path As String
  Dim RowWbks As Long
  Dim WbkName As String

  Path = ThisWorkbook.Path & "\"

  With Worksheets("Workbooks")
    .Cells.EntireRow.Delete
    .Cells(1, 1).Value = "New workbook"
    .Cells(1, 2).Value = "Existing workbooks"
    .Range("A1:B1").Font.Bold = True

    RowWbks = 2

    WbkName = Dir$(Path & "*.xls*", vbNormal)

    Do While WbkName <> ""
      If WbkName <> ThisWorkbook.Name Then
        .Cells(RowWbks, 2).Value = WbkName
        RowWbks = RowWbks + 1
      End If
      WbkName = Dir$
    Loop

    .Columns.AutoFit

  End With

End Sub

此代码属于“ModMerge”模块:

Option Explicit
  Const RowSrcDataFirst As Long = 3
Sub Merge()

  Dim ColCrnt As Long        ' Current column in either current worksheet
  Dim ColDestLast As Long    ' Last column in current destination worksheet
  Dim ColSrcLast As Long     ' Last column in current source worksheet
  Dim NumShtCrnt As Long     ' Number of current worksheet in source and destination workbooks
  Dim Path As String         ' Folder holding workbooks
  Dim RngSrc As Range        ' Range to be copied from current source worksheet
  Dim RowDestLast As Long    ' Last row in current destination worksheet
  Dim RowSrcCrnt As Long     ' Current row in current source worksheet
  Dim RowSrcLast As Long     ' Last row in current source worksheet
  Dim RowWbksCrnt As Long    ' Current row in worksheet "Workbooks"
  Dim WbkDest As Workbook    ' Reference to destination workbook
  Dim WbkSrc As Workbook     ' Reference to source workbook
  Dim WbkDestName As String  ' Name of destination workbook
  Dim WbkSrcName As String   ' Name of current source workbook
  Dim WshtDest As Worksheet  ' Reference to current destination worksheet
  Dim WshtSrc As Worksheet   ' Reference to current source worksheet
  Dim WshtWbks As Worksheet  ' Reference to worksheet "Workbooks"

  Application.ScreenUpdating = False

  Set WshtWbks = ThisWorkbook.Worksheets("Workbooks")
  Path = ThisWorkbook.Path & "\"

  Set WbkDest = Workbooks.Add    ' Create new empty workbook

  RowWbksCrnt = 2
  WbkDestName = WshtWbks.Cells(RowWbksCrnt, 1).Value

  Do While True  ' Loop until find blank line in worksheet WorkBooks

    ' Get name of next source workbook
    With WshtWbks
      WbkSrcName = .Cells(RowWbksCrnt, 2).Value
      If WbkSrcName = "" Then Exit Do
      RowWbksCrnt = RowWbksCrnt + 1
    End With

    Debug.Print WbkSrcName;

    ' Open current source workbook
    Set WbkSrc = Workbooks.Open(Path & WbkSrcName, False, False)

    ' Copy worksheets "Sheet1" to "SheetN" from current
    ' source workbook to destination workbook
    NumShtCrnt = 1
    Do While True  '  Loop until SheetN does not exist
      If Not WshtExists(WbkSrc, "Sheet" & NumShtCrnt) Then
        ' No more worksheets
        Exit Do
      End If
      Debug.Print "  " & NumShtCrnt;
      With WbkSrc
        Set WshtSrc = .Worksheets("Sheet" & NumShtCrnt)
      End With
      ' Create destination worksheet if it does not exist
      With WbkDest
        If Not WshtExists(WbkDest, "Sheet" & NumShtCrnt) Then
          Set WshtDest = .Worksheets.Add
          With WshtDest
            .Name = "Sheet" & NumShtCrnt
            .Move After:=WbkDest.Worksheets("Sheet" & NumShtCrnt - 1)
          End With
        Else
          Set WshtDest = .Worksheets("Sheet" & NumShtCrnt)
        End If
      End With
      'Debug.Print "  " & "Sheet" & NumShtCrnt
      ' Find dimensions of source worksheet
      Call FindLastRowCol(WshtSrc, RowSrcLast, ColSrcLast)
      ' Find last row of destination worksheet; do not need last column
      Call FindLastRowCol(WshtDest, RowDestLast, ColDestLast)
      'Debug.Print "  Src " & RowSrcLast & " " & ColSrcLast
      'Debug.Print "  Dest " & RowDestLast & " " & ColDestLast
      With WshtSrc
        If RowDestLast = 0 Then
          ' First source worksheet to be copied to this destination sheet
          ' Include header rows
          Set RngSrc = .Range(.Cells(1, 1), .Cells(RowSrcLast, ColSrcLast))
          ' Copy column widths
          For ColCrnt = 1 To ColSrcLast
            WshtDest.Columns(ColCrnt).ColumnWidth = WshtSrc.Columns(ColCrnt).ColumnWidth
          Next
        Else
          ' Not first worksheet so exclude header rows
          Set RngSrc = .Range(.Cells(RowSrcDataFirst, 1), .Cells(RowSrcLast, ColSrcLast))
        End If
      End With
      RngSrc.Copy Destination:=WshtDest.Cells(RowDestLast + 1, 1)
      NumShtCrnt = NumShtCrnt + 1
    Loop  ' for every worksheet in current source workbook
    WbkSrc.Close SaveChanges:=False
    Debug.Print
  Loop  ' for every source workbook

  WbkDest.Close SaveChanges:=True, Filename:=Path & WbkDestName

  Application.ScreenUpdating = False

End Sub

运行宏“准备”以在工作表“工作簿”中创建一个列表,如下所示:

由宏 Prepare 创建的工作表“工作簿”

我的文件夹中有四个数据工作簿; 你有十九个。 这些工作簿按创建的顺序列出,这可能不是您在新工作簿中想要的顺序。 将此工作表修改为如下所示:

工作表“工作簿”准备控制宏合并

A 列中的名称是新工作簿的名称。 我已将 B 列中的名称序列更改为我希望的 output 序列。 因此“Data B.xlsx”中的数据将首先出现,然后是“Data D.xlsx”中的数据,依此类推。 如果数据的顺序无关紧要,请不要理会 B 列。

一旦工作表“工作簿”如您所愿,运行宏“合并”。 在宏运行时,立即 Window 中出现以下内容,以提供粗略的进度指示器:

Data B.xlsx  1  2  3
Data D.xlsx  1  2  3
Data A.xlsx  1  2  3
Data C.xlsx  1  2  3

每个工作簿我只有三个工作表。 该宏处理与它找到的一样多的工作表名称“Sheets1”到“SheetsN”。 因此,对您而言,工作表编号列表将达到 16 个。您将拥有 19 个工作簿名称。 在我的笔记本电脑上,每个源工作表只用了几分之一秒,因此整个合并过程只用了很少的时间。

检查新数据工作簿后,将宏工作簿移动到下一个文件夹并再次运行准备和合并。 对每个文件夹重复。

宏中的注释告诉您每个代码块在做什么,而不是每个语句在做什么。 如有必要,请回来提出问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM