patternMinor
Importing a CSV file into Excel
Viewed 0 times
fileexcelintocsvimporting
Problem
I need help making this code run faster. I am using it to import a CSV file into Excel. It works, but it is very slow. The file is almost 20MB.
```
{Sub OpenTextFile()
Dim FilePath As String
Dim linitem As Variant
FilePath = "filepath.txt"
Open FilePath For Input As #1
row_number = 0
Do
Line Input #1, LineFromFile
LineItems = Split(LineFromFile, "|")
ActiveCell.Offset(row_number, 30).Value = LineItems(0)
ActiveCell.Offset(row_number, 29).Value = LineItems(1)
ActiveCell.Offset(row_number, 28).Value = LineItems(2)
ActiveCell.Offset(row_number, 27).Value = LineItems(3)
ActiveCell.Offset(row_number, 26).Value = LineItems(4)
ActiveCell.Offset(row_number, 25).Value = LineItems(5)
ActiveCell.Offset(row_number, 24).Value = LineItems(6)
ActiveCell.Offset(row_number, 23).Value = LineItems(7)
ActiveCell.Offset(row_number, 22).Value = LineItems(8)
ActiveCell.Offset(row_number, 21).Value = LineItems(9)
ActiveCell.Offset(row_number, 20).Value = LineItems(10)
ActiveCell.Offset(row_number, 19).Value = LineItems(11)
ActiveCell.Offset(row_number, 18).Value = LineItems(12)
ActiveCell.Offset(row_number, 17).Value = LineItems(13)
ActiveCell.Offset(row_number, 16).Value = LineItems(14)
ActiveCell.Offset(row_number, 15).Value = LineItems(15)
ActiveCell.Offset(row_number, 14).Value = LineItems(16)
ActiveCell.Offset(row_number, 13).Value = LineItems(17)
ActiveCell.Offset(row_number, 12).Value = LineItems(18)
ActiveCell.Offset(row_number, 11).Value = LineItems(19)
ActiveCell.Offset(row_number, 10).Value = LineItems(20)
ActiveCell.Offset(row_number, 9).Value = LineItems(21)
ActiveCell.Offset(row_number, 8).Value = LineItems(22)
ActiveCell.Offset(row_number, 7).Value = LineItems(23)
ActiveCell.Offset(row_number, 6).Value = LineItems(24)
ActiveCell.Offset(row_number, 5).Value = LineItems(25)
ActiveCell.Offset(row_number, 4).Value = LineItems(26)
ActiveCell.Offset(row_number, 3).Value = LineItems(27)
ActiveCell.Offset(row_number, 2).Value = LineItems(28)
ActiveCell.Offset(row_number, 1).Valu
```
{Sub OpenTextFile()
Dim FilePath As String
Dim linitem As Variant
FilePath = "filepath.txt"
Open FilePath For Input As #1
row_number = 0
Do
Line Input #1, LineFromFile
LineItems = Split(LineFromFile, "|")
ActiveCell.Offset(row_number, 30).Value = LineItems(0)
ActiveCell.Offset(row_number, 29).Value = LineItems(1)
ActiveCell.Offset(row_number, 28).Value = LineItems(2)
ActiveCell.Offset(row_number, 27).Value = LineItems(3)
ActiveCell.Offset(row_number, 26).Value = LineItems(4)
ActiveCell.Offset(row_number, 25).Value = LineItems(5)
ActiveCell.Offset(row_number, 24).Value = LineItems(6)
ActiveCell.Offset(row_number, 23).Value = LineItems(7)
ActiveCell.Offset(row_number, 22).Value = LineItems(8)
ActiveCell.Offset(row_number, 21).Value = LineItems(9)
ActiveCell.Offset(row_number, 20).Value = LineItems(10)
ActiveCell.Offset(row_number, 19).Value = LineItems(11)
ActiveCell.Offset(row_number, 18).Value = LineItems(12)
ActiveCell.Offset(row_number, 17).Value = LineItems(13)
ActiveCell.Offset(row_number, 16).Value = LineItems(14)
ActiveCell.Offset(row_number, 15).Value = LineItems(15)
ActiveCell.Offset(row_number, 14).Value = LineItems(16)
ActiveCell.Offset(row_number, 13).Value = LineItems(17)
ActiveCell.Offset(row_number, 12).Value = LineItems(18)
ActiveCell.Offset(row_number, 11).Value = LineItems(19)
ActiveCell.Offset(row_number, 10).Value = LineItems(20)
ActiveCell.Offset(row_number, 9).Value = LineItems(21)
ActiveCell.Offset(row_number, 8).Value = LineItems(22)
ActiveCell.Offset(row_number, 7).Value = LineItems(23)
ActiveCell.Offset(row_number, 6).Value = LineItems(24)
ActiveCell.Offset(row_number, 5).Value = LineItems(25)
ActiveCell.Offset(row_number, 4).Value = LineItems(26)
ActiveCell.Offset(row_number, 3).Value = LineItems(27)
ActiveCell.Offset(row_number, 2).Value = LineItems(28)
ActiveCell.Offset(row_number, 1).Valu
Solution
If I were to rewrite your code, I'd do it like this:
Step one, always be explicit. Nobody likes seeing a variable being used that they never saw declared anywhere.
Step two, name the procedure after what it's doing, and make it do that.
So the
The code under this label will execute even if no error occurred, this is why we're checking for
This small procedure takes two optional parameters. If none are specified, "wait mode" is toggled off - this means the status bar message is reset, the mouse cursor goes back to default, Excel resumes redrawing the screen and fires events whenever something happens. "Wait mode" is just all that reversed :)
This small procedure takes a single line of data, splits it into as many fields as it has, and writes each field value into a cell of the active sheet. Using
Local variables
Couple observations:
Option ExplicitStep one, always be explicit. Nobody likes seeing a variable being used that they never saw declared anywhere.
Public Sub ImportTextFile()Step two, name the procedure after what it's doing, and make it do that.
Dim filePath As String
filePath = "filepath.txt"
Dim fileNumber As Long
fileNumber = FreeFileFreeFile will give you a file number that's ok to use. Hard-coding a file number is never a good idea, even if you're only using one file.Dim inputData As String
Dim lineCount As Long
On Error Goto ErrHandler
ToggleWaitMode True, "Importing text file..."On Error Goto ErrHandler means if anything wrong happens (file not found, more data in the file than can fit on the worksheet, or whatever), we'll skip straight to an error-handling subroutine where we'll take good care of closing our file and toggle "wait mode" back off.Open filePath For Input As #fileNumber
Do Until EOF(fileNumber)
Line Input #fileNumber, inputData
lineCount = lineCount + 1
WriteLineContentToActiveSheet inputData, lineCount
Loop
Close #fileNumberSo the
ImportTextFile procedure does nothing but reading the file's content. WriteLineContentToActiveSheet will do the worksheet-writing part.ErrHandler:
ToggleWaitMode
If Err.Number <> 0 Then
Close 'closes any file left open
MsgBox Err.Description
Err.Clear
End If
End subThe code under this label will execute even if no error occurred, this is why we're checking for
Err.Number being other than 0. But regardless of whether there's an error or not, we always want to ToggleWaitMode.Private Sub ToggleWaitMode(Optional ByVal wait As Boolean = False, Optional ByVal statusBarMessage As String = vbNullString)
Application.ScreenUpdating = True 'ensure status message gets displayed
Application.StatusBar = statusBarMessage
Application.Cursor = IIf(wait, xlWait, xlDefault)
Application.ScreenUpdating = Not wait
Application.EnableEvents = Not Wait
End SubThis small procedure takes two optional parameters. If none are specified, "wait mode" is toggled off - this means the status bar message is reset, the mouse cursor goes back to default, Excel resumes redrawing the screen and fires events whenever something happens. "Wait mode" is just all that reversed :)
Private Sub WriteLineContentToActiveSheet(ByVal inputData As String, ByVal targetRow As Long)
Dim lineFields() As String
lineFields = Split(inputData, "|")
Dim fieldCount As Integer
fieldCount = UBound(lineFields)
Dim currentField As Integer
Dim targetColumn As Integer
Dim fieldValue As String
With ActiveSheet
For currentField = 0 To fieldCount
fieldValue = lineFields(currentField)
targetColumn = fieldCount - currentField + 1
.Cells(targetRow, targetColumn).Value = fieldValue
Next
End With
End SubThis small procedure takes a single line of data, splits it into as many fields as it has, and writes each field value into a cell of the active sheet. Using
UBound we're no longer tied to a specific number of fields - if the file format changes, the procedure should still work. And if it doesn't, the procedure that called it will cleanly handle the error for us.Local variables
currentField, targetColumn and fieldValue are not necessary - they could all be inlined, but having them makes code easier to follow and allows you to place breakpoints to validate their values as you run the code line-by-line with F8 when you're debugging.Couple observations:
- You need to indent your code. Indentation makes it much easier to read, for yourself and anyone looking at it. Give that Tab button some lovin'!
- Procedures define a scope, anything under it should be indented.
- Within a scope, code blocks should also be indented - this includes
Ifblocks,Withblocks and any code that runs inside a loop.
OpenTextFileis a bad name for a procedure that actually imports a text file.
- Be consistent with your naming: chose a convention, and stick to it.
- You're reading a file, that's an I/O operation and by Murphy's Law this is going to fail one day or another. Make sure your code properly handles any error that could happen while processing the file, you don't want to leave a file handle opened by mistake.
- Importing a 20mb text file will take a while, even with screen updating turned off. Tell your user you're working, give'em a hourglass cursor, and you can even update the statusbar message every couple thousand lines read/written (just call
ToggleWaitMode Truewith a new status message) so they know it's not frozen.
Code Snippets
Option ExplicitPublic Sub ImportTextFile()Dim filePath As String
filePath = "filepath.txt"
Dim fileNumber As Long
fileNumber = FreeFileDim inputData As String
Dim lineCount As Long
On Error Goto ErrHandler
ToggleWaitMode True, "Importing text file..."Open filePath For Input As #fileNumber
Do Until EOF(fileNumber)
Line Input #fileNumber, inputData
lineCount = lineCount + 1
WriteLineContentToActiveSheet inputData, lineCount
Loop
Close #fileNumberContext
StackExchange Code Review Q#33728, answer score: 7
Revisions (0)
No revisions yet.