Saving Excel data as csv with VBA - removing blank rows at end of file to save

15,610

Solution 1

You can get the UsedRange to recalculate itself without deleting columns and rows with a simple

ActiveSheet.UsedRange

Alternatively you can automate the manual removal of the "false" usedrange by deleting the areas below the last used cell with code such as DRJ's VBAexpress article, or by using an addin such as ASAP Utilities

The function from DRJ's article is;

Option Explicit 

Sub ExcelDiet() 

Dim j               As Long 
Dim k               As Long 
Dim LastRow         As Long 
Dim LastCol         As Long 
Dim ColFormula      As Range 
Dim RowFormula      As Range 
Dim ColValue        As Range 
Dim RowValue        As Range 
Dim Shp             As Shape 
Dim ws              As Worksheet 

Application.ScreenUpdating = False 
Application.DisplayAlerts = False 

On Error Resume Next 

For Each ws In Worksheets 
    With ws 
         'Find the last used cell with a formula and value
         'Search by Columns and Rows
        On Error Resume Next 
        Set ColFormula = .Cells.Find(What:="*", After:=Range("A1"), LookIn:=xlFormulas, _ 
        LookAt:=xlPart, SearchOrder:=xlByColumns, SearchDirection:=xlPrevious) 
        Set ColValue = .Cells.Find(What:="*", After:=Range("A1"), LookIn:=xlValues, _ 
        LookAt:=xlPart, SearchOrder:=xlByColumns, SearchDirection:=xlPrevious) 
        Set RowFormula = .Cells.Find(What:="*", After:=Range("A1"), LookIn:=xlFormulas, _ 
        LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlPrevious) 
        Set RowValue = .Cells.Find(What:="*", After:=Range("A1"), LookIn:=xlValues, _ 
        LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlPrevious) 
        On Error GoTo 0 

         'Determine the last column
        If ColFormula Is Nothing Then 
            LastCol = 0 
        Else 
            LastCol = ColFormula.Column 
        End If 
        If Not ColValue Is Nothing Then 
            LastCol = Application.WorksheetFunction.Max(LastCol, ColValue.Column) 
        End If 

         'Determine the last row
        If RowFormula Is Nothing Then 
            LastRow = 0 
        Else 
            LastRow = RowFormula.Row 
        End If 
        If Not RowValue Is Nothing Then 
            LastRow = Application.WorksheetFunction.Max(LastRow, RowValue.Row) 
        End If 

         'Determine if any shapes are beyond the last row and last column
        For Each Shp In .Shapes 
            j = 0 
            k = 0 
            On Error Resume Next 
            j = Shp.TopLeftCell.Row 
            k = Shp.TopLeftCell.Column 
            On Error GoTo 0 
            If j > 0 And k > 0 Then 
                Do Until .Cells(j, k).Top > Shp.Top + Shp.Height 
                    j = j + 1 
                Loop 
                If j > LastRow Then 
                    LastRow = j 
                End If 
                Do Until .Cells(j, k).Left > Shp.Left + Shp.Width 
                    k = k + 1 
                Loop 
                If k > LastCol Then 
                    LastCol = k 
                End If 
            End If 
        Next 

        .Range(.Cells(1, LastCol + 1), .Cells(.Rows.Count, .Columns.Count)).EntireColumn.Delete 
        .Range("A" & LastRow + 1 & ":A" & .Rows.Count).EntireRow.Delete 
    End With 
Next 

Application.ScreenUpdating = True 
Application.DisplayAlerts = True 

End Sub 

Solution 2

Excel saves the UsedRange. In order to truncate the UsedRange, you need to delete whole rows and save the file.

If that's not an option, insert a new worksheet, copy the prepared data to it (thus leaving its UsedRange matching actual data), use Worksheet.SaveAs (as opposed to Workbook.SaveAs) and delete the worksheet.

Although the actual problem here is why your UsedRange gets that big in the first place.

Share:
15,610
David Manheim
Author by

David Manheim

Links I wanted to put here: Meta - http://meta.stackexchange.com/questions/128548/what-stack-overflow-is-not/128553#128553

Updated on June 27, 2022

Comments

  • David Manheim
    David Manheim almost 2 years

    I am creating a set of csv files in VBA.

    My script is creating the data set I need, but the number of rows differs in multiple iterations of the loop. For instance, for i=2, I have 100,000 rows, but for i=3, I have 22,000 rows. The problem is that when Excel saves these separate csv files, it does not truncate the space at the end. This leaves 78,000 blank rows at the end of the file, which is an issue given that I need about 2,000 files to be generated, each several megabytes large. (I have some data I need in SQL, but can't do the math in SQL itself. Long story.)

    This problem normally occurs when saving manually - you need to close the file after removing the rows, then reopen, which is not an option in this case, since it's happening automatically in VBA. Removing the blank rows after saving using a script in another language isn't really an option, since I actually need the output files to fit on the drive available, and they are unnecessarily huge now.

    I have tried Sheets(1).Range("A2:F1000001").ClearContents, but this does not truncate anything. Removing the rows should have similarly no effect before saving, since Excel saves all rows until the end of the file, as it stores the bottom-right most cell operated on. Is there a way to have excel save only the rows I need?

    Here is my code used to save: (The truncation happens earlier, in the routing that calls this one)

    Sub SaveCSV()
    'Save the file as a CSV...
      Dim OutputFile As Variant
      Dim FilePath As Variant
    
      OutputPath = ActiveWorkbook.Worksheets("Macro").Range("B2").Value
      OutputFile = OutputPath & ActiveWorkbook.Worksheets("Macro").Range("B1").Value
      Application.DisplayAlerts = False 'DISABLE ALERT on Save - overwrite, etc.
      ActiveWorkbook.SaveAs Filename:=OutputFile, FileFormat:=xlCSV, CreateBackup:=False
      Application.DisplayAlerts = True 'DISPLAY ALERTS
    End Sub
    

    The relevant bit of code is here:

    'While looping through Al, inside of looping through A and B...
    'Created output values needed in this case, in an array...
    
    Sheets(1).Range("A2:E90001") = Output
    
    ActiveWorkbook.Worksheets(1).Range("F2").Formula = "=(does not matter, some formula)"
    ActiveWorkbook.Worksheets(1).Range("F2").AutoFill Destination:=Range("F2:F90001")
    
    'Set Filename to save into...
    ActiveWorkbook.Worksheets("Macro").Range("B1").Value = "Values_AP" & Format(A, "#") & "_BP" & Format(B, "#") & "_Al" & Format(Al, "#")
    
    'Save Sheet and reset...
    Call SaveCSV
    Sheets(1).Range("A2:F90001").ClearContents
    CurrRow = 1
    
    Next Al
    
  • David Manheim
    David Manheim almost 12 years
    Thank you for the suggestions. I cannot save the file without saving it as something - and the current file name is a csv that would overwrite the previous file. The Usedrange varies because, within the loop, some items have up to 1m rows output, and following items in the loop have many fewer. I will try inserting a new worksheet in order to save that worksheet, then removing it - but I am worried this will slow down the Macro immensely.
  • aelveborn
    aelveborn almost 12 years
    @DavidManheim You can also try to save the original file as an xls file into the system temp folder, truncating the usedrange, then immediately save it again as csv.
  • David Manheim
    David Manheim almost 12 years
    That may be a better solution. Thanks.