拆分具有多行文本和单行文本的行

Split rows that have multiline text and single line text

我想弄清楚如何拆分行中 B、C、D 列包含多行而其他列不包含的数据行。我已经想出了如何拆分多行单元格,如果我只将这些列复制到一个新的 sheet,手动插入行,然后 运行 下面的宏(仅适用于 A 列),但我对剩下的代码一无所知。

数据如下:

因此,对于第 2 行,我需要将其分成 6 行(单元格 B2 中的每一行),文本在 A2:A8 中的单元格 A2 中。我还需要 C 列和 D 列与 B 拆分相同,然后 列 E:CP 与 A 列相同。

这是我用于拆分 B、C、D 列中的单元格的代码:

Dim iPtr As Integer
Dim iBreak As Integer
Dim myVar As Integer
Dim strTemp As String
Dim iRow As Integer
iRow = 0
For iPtr = 1 To Cells(Rows.Count, col).End(xlUp).Row
    strTemp = Cells(iPtr1, 1)
    iBreak = InStr(strTemp, vbLf)
    Range("C1").Value = iBreak
        Do Until iBreak = 0
        If Len(Trim(Left(strTemp, iBreak - 1))) > 0 Then
            iRow = iRow + 1
            Cells(iRow, 2) = Left(strTemp, iBreak - 1)
        End If
        strTemp = Mid(strTemp, iBreak + 1)
        iBreak = InStr(strTemp, vbLf)
    Loop
    If Len(Trim(strTemp)) > 0 Then
        iRow = iRow + 1
        Cells(iRow, 2) = strTemp
    End If
Next iPtr
End Sub

这是一个link示例文件(注意这个文件有4行,实际sheet有600多行):https://www.dropbox.com/s/46j9ks9q43gwzo4/Example%20Data.xlsx?dl=0

感谢您提供样本。这个任务非常有趣,我想到了为此编写代码。非常欢迎您对其进行调整,直到您满意为止,我希望您的团队将来能够使用 RDBMS 来管理此类数据。

Sub OrganizeSheet()

    Dim LastRow As Integer
    LastRow = GetLastRow()

    Dim Barray() As String
    Dim Carray() As String
    Dim Darray() As String
    Dim LongestArray As Integer
    Dim TempInt As Integer

    Dim i As Integer
    i = 1

    Do While i <= LastRow

        Barray = Split(Range("B" & i), Chr(10))
        Carray = Split(Range("C" & i), Chr(10))
        Darray = Split(Range("D" & i), Chr(10))
        LongestArray = GetLongestArray(Barray, Carray, Darray)

        If LongestArray > 0 Then

            ' reset the values of B, C and D columns
            On Error Resume Next
            Range("B" & i).Value = Barray(0)
            Range("C" & i).Value = Carray(0)
            Range("D" & i).Value = Darray(0)
            Err.Clear
            On Error GoTo 0

            ' duplicate the row multiple times
            For TempInt = 1 To LongestArray

                Rows(i & ":" & i).Select
                Selection.Copy

                Range(i + TempInt & ":" & i + TempInt).Select
                Selection.Insert Shift:=xlDown

                ' as each row is copied, change the values of B, C and D columns
                On Error Resume Next
                Range("B" & i + TempInt).Value = Barray(TempInt)
                If Err.Number > 0 Then Range("B" & i + TempInt).Value = ""
                Err.Clear
                Range("C" & i + TempInt).Value = Carray(TempInt)
                If Err.Number > 0 Then Range("C" & i + TempInt).Value = ""
                Err.Clear
                Range("D" & i + TempInt).Value = Darray(TempInt)
                If Err.Number > 0 Then Range("D" & i + TempInt).Value = ""
                Err.Clear
                On Error GoTo 0

                Application.CutCopyMode = False

            Next TempInt

            ' increment the outer FOR loop's counters
            LastRow = LastRow + LongestArray
            i = i + LongestArray

        End If

        i = i + 1
    Loop

End Sub

' ----------------------------------

Function GetLongestArray(ByRef Barray() As String, ByRef Carray() As String, ByRef Darray() As String)
    GetLongestArray = UBound(Barray)
    If UBound(Carray) > GetLongestArray Then GetLongestArray = UBound(Carray)
    If UBound(Darray) > GetLongestArray Then GetLongestArray = UBound(Darray)
End Function

' ----------------------------------

Function GetLastRow() As Integer
    Worksheets(1).Select
    Range("A1").Select
    Selection.End(xlDown).Select
    GetLastRow = Selection.Row
    Range("A1").Select
End Function

试一试!

这是一个相当有趣的问题,也是我以前见过的变体。我继续为它编写了一个通用解决方案,因为它似乎是我自己保留的有用代码。

我对数据几乎只有两个假设:

  • Returns 表示为 Chr(10)vbLf 常量。
  • 属于较低行的数据中有足够的 return 使其排成一行。这似乎是你的情况,因为有 return 个字符似乎让事情按照你想要的方式排列。

输出图片,缩小以显示 A:D 的所有数据。请注意,下面的代码 默认处理所有列并输出到新的 sheet。如果需要,您可以限制列数,但想将其设为通用。

代码

Sub SplitByRowsAndFillBlanks()

    'process the whole sheet, could be
    'Intersect(Range("B:D"), ActiveSheet.UsedRange)
    'if you just want those columns
    Dim rng_all_data As Range
    Set rng_all_data = Range("A1").CurrentRegion

    Dim int_row As Integer
    int_row = 0

    'create new sheet for output
    Dim sht_out As Worksheet
    Set sht_out = Worksheets.Add

    Dim rng_row As Range
    For Each rng_row In rng_all_data.Rows

        Dim int_col As Integer
        int_col = 0

        Dim int_max_splits As Integer
        int_max_splits = 0

        Dim rng_col As Range
        For Each rng_col In rng_row.Columns

            'splits for current column
            Dim col_parts As Variant
            col_parts = Split(rng_col, vbLf)

            'check if new max row count
            If UBound(col_parts) > int_max_splits Then
                int_max_splits = UBound(col_parts)
            End If

            'fill the data into the new sheet, tranpose row array to columns
            sht_out.Range("A1").Offset(int_row, int_col).Resize(UBound(col_parts) + 1) = Application.Transpose(col_parts)

            int_col = int_col + 1
        Next

        'max sure new rows added for total length
        int_row = int_row + int_max_splits + 1
    Next

    'go through all blank cells and fill with value from above
    Dim rng_blank As Range
    For Each rng_blank In sht_out.Cells.SpecialCells(xlCellTypeBlanks)
        rng_blank = rng_blank.End(xlUp)
    Next

End Sub

工作原理

代码中有注释以突出显示正在发生的事情。这是一个高级概述:

  • 总的来说,我们遍历每一行数据,单独处理所有列。
  • 当前单元格的文本是 Split 使用 vbLf。这给出了所有单独行的数组。
  • 一个计数器正在跟踪添加的最大行数(实际上这是 rows-1 因为这些数组是 0-indexed.
  • 现在数据可以输出到新的sheet。这很容易,因为我们可以转储 Split 为我们创建的数组。唯一棘手的部分是将它放到 sheet 上的正确位置。为此,有一个用于当前列偏移量的计数器和一个用于确定需要偏移的总行数的全局计数器。 Offset 将我们移动到正确的单元格; Resize 确保输出所有行。最后,需要 Application.Transpose,因为 Split return 是一个行数组,我们要转储一列。
  • 更新计数器。每次都会增加列偏移量。更新行偏移以添加足够的行来覆盖最后的最大值(+1 因为这是 0-indexed
  • 最后,我对所有为确保没有空白而创建的空白单元格进行 。我放弃错误检查,因为我假设存在空白。