1

I have a macro which splits cells with multiple lines of data at line breaks, it works smoothly. However I've hit a bump that the macro leaves some blank cells while splitting. I have certain code to identify the blank cells and delete them, but somehow it doesn't work. So I wonder do I put the testing blank code at the right position? They seem to be correct code for checking blank code though.

Here's my code:

Sub SplitMultipleHostnames()
Dim tmpArr As Variant


For Each cell In Range("D2", Range("D3").End(xlDown))
If cell <> "" Then
    If InStr(1, cell, Chr(10)) <> 0 Then
        tmpArr = Split(cell, Chr(10))

        cell.EntireRow.Copy

        cell.Offset(1, 0).Resize(UBound(tmpArr), 1).EntireRow.Insert xlShiftDown



        cell.Resize(UBound(tmpArr) + 1, 1) = Application.Transpose(tmpArr)


    End If
Else
    cell.EntireRow.Delete
End If
Next
Application.CutCopyMode = False


End Sub

Here's a print screen of the sample, usually hostnames and ip addresses will be line by line, but if in between there's a empty line, it will be split as a blank cell. And the blank cell will stop the whole loop working.

Edit: Noted that some blank cells are created while the code running. enter image description here

vivi xu
  • 147
  • 1
  • 15
  • 1
    This code only splits cells that are not empty. Which is also what it should do, since it should split at chr(10), which can't be done when the cell is empty. Perhaps you need to check first if there are multiple Chr(10) right after each other. The same thing as the build in dialogue for splitting cells can do: treat consequitive delimiters as one. – Luuklag Aug 05 '15 at 08:41
  • @Luuklag There is, and I need to delete the empty rows with any empty cells too. – vivi xu Aug 05 '15 at 08:53
  • perhaps you can show us a print screen of where exactly empty cells end up, and what the original input was for that row. – Luuklag Aug 05 '15 at 08:57
  • @Luuklag Actually now the biggest problem is the empty row, cuz it stops the loop. I will upload the print screen soon. – vivi xu Aug 05 '15 at 09:10
  • @Luuklag Hi just uploaded the screenshot – vivi xu Aug 05 '15 at 09:32
  • Does it delete the first empty cell or not? If yes, it could be that by deleting the cell the order in which cells are handled is corrupted. I usually do this kind of work with a For loop, and then reference to the rows as rows, and the cells as Cell(row,column). Then when one row gets deleted you need to do that same row again, so after cell delete you need to have something like `row = row-1` – Luuklag Aug 05 '15 at 09:38
  • @Luuklag no, the problem is it's not deleting any empty cells, should I try with `row = row-1` now? But my condition should already delete the whole row – vivi xu Aug 05 '15 at 09:42
  • Yes it deletes rows, but please run the code step by step and see if it deletes the first row that contains an empty cell or not. – Luuklag Aug 05 '15 at 10:07
  • @Luuklag if you are deleting rows in a for loop, you can avoid complicating things by iterating over the range in reverse order. E.g. `For i = SomeRange.Rows.Count to 1 Step -1` – CBRF23 Aug 05 '15 at 10:43
  • @CBRF23 I'm putting in a For loop, wondering why it can't identify and delete the empty cells – vivi xu Aug 05 '15 at 11:54
  • @CBRF23: Thanks for the tip, that would indeed save a lot of work :) – Luuklag Aug 06 '15 at 06:26

2 Answers2

0

Maybe the cells aren't really empty as proposed in an earlier comment. Did you verify that these cells are really empty?

If Replace(Replace(Trim(cell.value2), chr(10), ""), vbNewLine, "") <> vbNullString Then
Ralph
  • 9,284
  • 4
  • 32
  • 42
  • If my solution does not solve the problem of identifying "empty cells", maybe this code can: http://stackoverflow.com/questions/16246078/remove-line-breaks-return-carriages-and-all-leading-space-in-excel-cell – Ralph Aug 05 '15 at 12:11
0

Try this code. I've commented it to explain what I'm doing, but let me know if you need a further breakdown of how it works. I've added error handling to deal with expected and unexpected conditions.

EDIT: I've added a function for cleaning cells created from concurrent delimiters in the split/transpose loop, and moved the delete empty rows step to after the do loop do clean these up.

Sub SplitMultipleHostnames()
    'I've added some error handling. 
    On Error GoTo UnexpectedErr

    'Get the last used cell.
    With Range("D:D")
        Dim LastDataCell As Range
        Set LastDataCell = .Find("*", .Cells(1, 1), xlFormulas, xlPart, xlByRows, xlPrevious, False, False, False)
    End With

    'We need to have some data to work upon beyond the first row
    If LastDataCell Is Nothing Then Exit Sub
    If LastDataCell.Row < 2 Then Exit Sub

    'I set a range variable here to make it easier to work with later
    Dim WorkingRange As Range
    Set WorkingRange = Range("D2", LastDataCell)

    'You can avoid expensive loop overhead by just finding the cells containing _
    the character you wish to split upon, and acting upon only these cells
    Dim FoundCell As Range
    Dim FirstAddress As String
    Set FoundCell = WorkingRange.Find(Chr(10), LastDataCell, xlValues, xlPart, xlByRows, xlNext, False, False, False)

    'Another benefit of this approach: if there are no line breaks we can exit early
    If FoundCell Is Nothing Then Exit Sub

    'We are going to use .FindNext to loop through all the cells containing our _
    delmiiter character.  Store first found cell's address so we know when we're done
    FirstAddress = FoundCell.Address

    'Since we know our data type we should declare are variable as such
    Dim tmpArr() As String
    Do
        tmpArr = Split(FoundCell, Chr(10))
        'Use a With block if you're lazy like me ;)
        With FoundCell
            .Offset(1, 0).Resize(UBound(tmpArr), 1).EntireRow.Insert xlShiftDown
            Set FoundCell = FoundCell.Resize(UBound(tmpArr) + 1, 1)
            FoundCell = Application.Transpose(tmpArr)
        End With

        'A cell could contain mulitple concurrent delimiters.
        'We can handle this by finding all concurrent delimiters and replacing with a _
        single delimiter before splitting the cell contents.
        ' - OR -
        'We can split the cell contents and then remove any remaining delimiters afterward

        'I've elected to do the latter as I think it's the simplest route in this application
        Dim CheckCell As Range
        For Each CheckCell In FoundCell
            RemoveDelimiters CheckCell, Chr(10), True
        Next

        'Find the next cell to work upon
        Set FoundCell = WorkingRange.FindNext
        'If we don't find another match, we are done
        If FoundCell Is Nothing Then Exit Do
    Loop While FoundCell.Address <> FirstAddress

    'Now that we are done, we will delete any rows with blank cells
    Dim BlankCells As Range

    'It is possible there are not any empty cells - we should anticipate this error and provide a way to handle it:
    On Error GoTo CatchErr001
    Set BlankCells = WorkingRange.SpecialCells(xlCellTypeBlanks)
    On Error GoTo UnexpectedErr

    'We check condition to see if BlankCells is allocated, as we know it won't be if no blank cells were found
    If Not BlankCells Is Nothing Then BlankCells.EntireRow.Delete

Exit Sub

CatchErr001:
    '1004 is a generic runtime error. It could be because no blank cells found, or something else.
    'If it's due to no blank cells our code is built to deal with this condition so we can safely swallow the error
    If (err.Number = 1004) And (InStr(1, err.Description, "No cells were found", vbTextCompare) > 0) Then
        Resume Next
    'If it's due to something else, our program is in an unknonw state.  This is unexpected
    Else
        GoTo UnexpectedErr
    End If

UnexpectedErr:
    Dim CaughtErr As ErrObject
    Set CaughtErr = err
    On Error GoTo 0
    err.Raise CaughtErr.Number, CaughtErr.Source, CaughtErr.Description, CaughtErr.HelpFile, CaughtErr.HelpContext
End Sub

Private Sub RemoveDelimiters(ByRef CheckCell As Range, ByRef Delimiter As String, Optional ByVal RemoveSpaces As Boolean = False)
    Dim CheckValue As String
    CheckValue = CheckCell.value
    'If the cell is already empty we don't do anything further
    If Len(CheckCell) <= 0 Then Exit Sub
    'Remove spaces if the calling procedure specified to do so
    If RemoveSpaces Then CheckValue = Trim(CheckValue)
    'Remove all delimiter characters
    CheckValue = Replace(CheckCell, Delimiter, "")
    'Replace the cell's value with our modified value
    CheckCell.value = CheckValue
End Sub
CBRF23
  • 1,340
  • 1
  • 16
  • 44
  • Hi thanks for your efforts on this answer, however it didn't either skip the blank cells or delete the rows with empty cells. – vivi xu Aug 06 '15 at 09:22
  • And fyi, some empty cells are created in the for loop, so you can't identify them beforehand. – vivi xu Aug 06 '15 at 09:26
  • I didn't know you wanted to skip blank cells - can you further explain the requirement? I though you wanted all blanks deleted. If blanks are created from the extraction process, you can simply move the delete portion of code to after the loop. If a row is not deleted, the cell is not empty. Select the cell in question, in the immediate window type `?IsEmpty(Selection)` - do you get true or false? – CBRF23 Aug 06 '15 at 10:45
  • I still don't know what you mean by skipping blank cells - but I've edited my answer to deal with what I think your problem is. Before running this - I'd still like you to perform the test I commented about above in the immediate window. If you get a return of `False` then try `?InStr(1,Selection.Value,chr(10))` and I'm going to guess you get something greater than 0. – CBRF23 Aug 06 '15 at 13:54
  • At OP - has your problem been solved? Did you try any of the things I suggested in the immediate window? – CBRF23 Aug 11 '15 at 11:08