I would really appreciate it if someone could give me some help with this.
I am quite familiar with vba and I can write simple code and also customise code from others. I have written /customised/copied several pieces of vba code to do the following (where copied source is acknowledged):
Select 2 different csv files which represent 2 matrixes of same columns and same rows.
Multiply each respective cells from the matrices.
Return results.
Unfortunately I cannot seem to be able to get this to run.
Any idea what I have not done correctly?
Please see the code below. Thanks so much.
Code changed from previous version
Public Sub doIt()
Dim sourceFile As String
Dim destinationFile As String
Dim data As Variant
Dim result As Variant
Dim sourceFile2 As String
Dim datarain As Variant
sourceFile = "C:\file1.csv"
sourceFile2 = "C:\file2.csv"
destinationFile = "C:\file3.txt"
data = getDataFromFile(sourceFile, ",")
datarain = getDataFromFile(sourceFile2, ",")
If Not isArrayEmpty(data) Then
result = MMULT2_FUNC(data, datarain)
writeToCsv result, destinationFile, ","
Else
MsgBox ("Empty file")
End If
End Sub
Function MMULT2_FUNC(ByRef ADATA_RNG As Variant, _
ByRef BDATA_RNG As Variant)
Dim i As Long
Dim j As Long
Dim k As Long
Dim ANROWS As Long
Dim BNROWS As Long
Dim ANCOLUMNS As Long
Dim BNCOLUMNS As Long
Dim ADATA_MATRIX As Variant
Dim BDATA_MATRIX As Variant
Dim TEMP_MATRIX As Variant
On Error GoTo ERROR_LABEL
ADATA_MATRIX = ADATA_RNG
BDATA_MATRIX = BDATA_RNG
ANROWS = UBound(ADATA_MATRIX, 1)
BNROWS = UBound(BDATA_MATRIX, 1)
ANCOLUMNS = UBound(ADATA_MATRIX, 2)
BNCOLUMNS = UBound(BDATA_MATRIX, 2)
If ANCOLUMNS <> BNROWS Then: GoTo ERROR_LABEL
ReDim TEMP_MATRIX(1 To ANROWS, 1 To BNCOLUMNS)
For i = 1 To ANROWS
For j = 1 To BNCOLUMNS
TEMP_MATRIX(i, j) = 0
For k = 1 To ANCOLUMNS
TEMP_MATRIX(i, j) = TEMP_MATRIX(i, j) + ADATA_MATRIX(i, k) * _
BDATA_MATRIX(k, j)
Next k
Next j
Next i
MMULT2_FUNC = TEMP_MATRIX
Exit Function
ERROR_LABEL:
MMULT2_FUNC = Err.Number
End Function
Public Sub writeToCsv(parData As Variant, parFileName As String, parDelimiter As String)
If getArrayNumberOfDimensions(parData) <> 2 Then Exit Sub
Dim i As Long
Dim j As Long
Dim FileNum As Long
Dim locLine As String
Dim locCsvString As String
FileNum = FreeFile
If Dir(parFileName) <> "" Then Kill (parFileName)
Open parFileName For Binary Lock Read Write As #FileNum
For i = LBound(parData, 1) To UBound(parData, 1)
locLine = ""
For j = LBound(parData, 2) To UBound(parData, 2)
If IsError(parData(i, j)) Then
locLine = locLine & "#N/A" & parDelimiter
Else
locLine = locLine & parData(i, j) & parDelimiter
End If
Next j
locLine = Left(locLine, Len(locLine) - 1)
If i <> UBound(parData, 1) Then locLine = locLine & vbCrLf
Put #FileNum, , locLine
Next i
error_handler:
Close #FileNum
End Sub
Public Function isArrayEmpty(parArray As Variant) As Boolean
'Returns false if not an array or dynamic array that has not been initialised (ReDim) or has been erased (Erase)
If IsArray(parArray) = False Then isArrayEmpty = True
On Error Resume Next
If UBound(parArray) < LBound(parArray) Then isArrayEmpty = True: Exit Function Else: isArrayEmpty = False
End Function
Public Function getArrayNumberOfDimensions(parArray As Variant) As Long
'Returns the number of dimension of an array - 0 for an empty array.
Dim i As Long
Dim errorCheck As Long
If isArrayEmpty(parArray) Then Exit Function 'returns 0
On Error GoTo FinalDimension
'Visual Basic for Applications arrays can have up to 60000 dimensions
For i = 1 To 60001
errorCheck = LBound(parArray, i)
Next i
'Not supposed to happen
getArrayNumberOfDimensions = 0
Exit Function
FinalDimension:
getArrayNumberOfDimensions = i - 1
End Function
Private Function getDataFromFile(parFileName As String, parDelimiter As String, Optional parExcludeCharacter As String = "") As Variant
'parFileName is supposed to be a delimited file (csv...)
'parDelimiter is the delimiter, "," for example in a comma delimited file
'Returns an empty array if file is empty or can't be opened
'number of columns based on the line with the largest number of columns, not on the first line
'parExcludeCharacter: sometimes csv files have quotes around strings: "XXX" - if parExcludeCharacter = """" then removes the quotes
Dim locLinesList() As Variant
Dim locData As Variant
Dim i As Long
Dim j As Long
Dim locNumRows As Long
Dim locNumCols As Long
Dim fso As Variant
Dim ts As Variant
Const REDIM_STEP = 10000
Set fso = CreateObject("Scripting.FileSystemObject")
On Error GoTo error_open_file
Set ts = fso.OpenTextFile(parFileName)
On Error GoTo unhandled_error
'Counts the number of lines and the largest number of columns
ReDim locLinesList(1 To 1) As Variant
i = 0
Do While Not ts.AtEndOfStream
If i Mod REDIM_STEP = 0 Then
ReDim Preserve locLinesList(1 To UBound(locLinesList, 1) + REDIM_STEP) As Variant
End If
locLinesList(i + 1) = Split(ts.ReadLine, parDelimiter)
j = UBound(locLinesList(i + 1), 1) 'number of columns
If locNumCols < j Then locNumCols = j
If j = 13 Then
j = j
End If
i = i + 1
Loop
ts.Close
locNumRows = i
If locNumRows = 0 Then Exit Function 'Empty file
ReDim locData(1 To locNumRows, 1 To locNumCols + 1) As Variant
'Copies the file into an array
If parExcludeCharacter <> "" Then
For i = 1 To locNumRows
For j = 0 To UBound(locLinesList(i), 1)
If Left(locLinesList(i)(j), 1) = parExcludeCharacter Then
If Right(locLinesList(i)(j), 1) = parExcludeCharacter Then
locLinesList(i)(j) = Mid(locLinesList(i)(j), 2, Len(locLinesList(i)(j)) - 2) 'If locTempArray = "", Mid returns ""
Else
locLinesList(i)(j) = Right(locLinesList(i)(j), Len(locLinesList(i)(j)) - 1)
End If
ElseIf Right(locLinesList(i)(j), 1) = parExcludeCharacter Then
locLinesList(i)(j) = Left(locLinesList(i)(j), Len(locLinesList(i)(j)) - 1)
End If
locData(i, j + 1) = locLinesList(i)(j)
Next j
Next i
Else
For i = 1 To locNumRows
For j = 0 To UBound(locLinesList(i), 1)
locData(i, j + 1) = locLinesList(i)(j)
Next j
Next i
End If
getDataFromFile = locData
Exit Function
error_open_file: 'returns empty variant
unhandled_error: 'returns empty variant
End Function
Despite my personal impression that your code can be improved in some instances, it syntactically executes here with no problem (on small matrices).
My test data
1,2,3 2,3,4 20,26,32
2,3,4 X 3,4,5 = 29,38,47
3,4,5 4,5,6 38,50,62
The result is neatly written to a CSV.
Only obvious problem (here on Win 7 !) is that Sub writeToCsv -> Open parFileName ... fails due to lack of write permissions into the root directory. This might be not a problem on XP.
On a different token, I have the impression the code can be improved, but I may not understand the rationale behind some parts of your code.
examples
Function MMULT2_FUNC(ByRef ADATA_RNG As Variant, ByRef BDATA_RNG As Variant) ' missing type of result
Private Function getDataFromFile(...)
...
If j = 13 Then
j = j
End If ' whow ... if j <> 13 then j again equals j ;-)
finding upper and lower bounds of the matrices on input as well as on output could be simplified by large ...
Thank you all for your help. The reason why my code was not printing results was that I had this:If ANCOLUMNS <> BNROWS Then: GoTo ERROR_LABEL. At the same time, I was using two matrices of 70*120, so it constantly exited the function as I had programmed it to do!!Corrected it all and worked fine. Thanks a lot for your help
Related
I have an Excel sheet like below and I need only the three "Break" times even if it meant to delete every thing except those three Breaks in every cell.
Function GetBreaksTime(txt As String)
Dim i As Long
Dim arr As Variant
arr = Split(txt, "Break")
If UBound(arr) > 0 Then
ReDim startTimes(1 To UBound(arr)) As String
For i = 1 To UBound(arr)
startTimes(i) = WorksheetFunction.Trim(Replace(Split(arr(i), "-")(0), vbLf, ""))
Next
GetBreaksTime = startTimes
End If
End Function
This what I got until now but it wont work on every cell and it takes wrong values.
So any idea how to do this?
If you split the cell value by vbLf the break time will always follow a line containing "Break".
The following should work:
Sub TestGetBreakTimes()
Dim CellValue As String
CellValue = Worksheets("Sheet1").Range("A1").Value
Dim BreakTimes As Variant
BreakTimes = GetBreakTimes(CellValue)
Debug.Print Join(BreakTimes, vbLf) 'the join is just to output the array at once.
'to output in different cells loop through the array
Dim i As Long
For i = 0 To UBound(BreakTimes)
Cells(3 + i, "A") = BreakTimes(i)
Next i
'or for a even faster output use
Range("A3").Resize(UBound(BreakTimes) + 1).Value = WorksheetFunction.Transpose(BreakTimes)
End Sub
Function GetBreakTimes(InputData As String) As Variant
Dim BreakTimes() As Variant
ReDim BreakTimes(0)
Dim SplitArr As Variant
SplitArr = Split(InputData, vbLf) 'split by line break
If UBound(SplitArr) > 0 Then
Dim i As Long
For i = 0 To UBound(SplitArr)
If SplitArr(i) = "Break" Then 'if line contains break then next line is the time of the break
If BreakTimes(0) <> vbNullString Then ReDim Preserve BreakTimes(UBound(BreakTimes) + 1)
BreakTimes(UBound(BreakTimes)) = SplitArr(i - 1) 'collect break time
End If
Next i
GetBreakTimes = BreakTimes
End If
End Function
To analyze a complete range you must loop through your row 2
Sub GetAllBreakTimes()
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Sheet1")
Dim LastCol As Long
LastCol = ws.Cells(2, ws.Columns.Count).End(xlToLeft).Column
Dim BreakTimes As Variant
Dim iCol As Long
For iCol = 1 To LastCol
BreakTimes = GetBreakTimes(ws.Cells(2, iCol).Value)
ws.Cells(3, iCol).Resize(UBound(BreakTimes) + 1).Value = WorksheetFunction.Transpose(BreakTimes)
Next iCol
End Sub
I have a csv file with several values: strings, dates, numbers...
A macro (Excel) imports the csv data to a Excel Sheet, all going well but...
In the csv file I have a column for dates like: "8/1/2018" (where 5 is day, 1 is month. Format: d/M/Y), but when I go to the imported data, I see the date like: "1/8/2018" (note day and month permuted). That's annoying because my Regional Window configuration specify the format: d/M/Y.
Here you can see the line where all happens: bad date conversion:
How could I import date data with correct format? Perhaps some dateFormat for the Sheet?
EDIT
Here is the code where the file get opened:
Sub ImportFile()
Dim sPath As String
'Below we assume that the file, csvtest.csv,
'is in the same folder as the workbook. If
'you want something more flexible, you can
'use Application.GetOpenFilename to get a
'file open dialogue that returns the name
'of the selected file.
'On the page Fast text file import
'I show how to do that - just replace the
'file pattern "txt" with "csv".
sPath = ThisWorkbook.Path & "\2018w02_wbt_exito.csv"
'Procedure call. Semicolon is defined as separator,
'and data is to be inserted on "Sheet2".
'Of course you could also read the separator
'and sheet name from the worksheet or an input
'box. There are several options.
copyDataFromCsvFileToSheet sPath, ";", """", "Hoja1"
End Sub
'**************************************************************
Private Sub copyDataFromCsvFileToSheet(parFileName As String, _
parDelimiter As String, parExcludeCharacter As String, parSheetName As String)
Dim Data As Variant 'Array for the file values
'Function call - the file is read into the array
Data = getDataFromFile(parFileName, parDelimiter, parExcludeCharacter)
'If the array isn't empty it is inserted into
'the sheet in one swift operation.
If Not isArrayEmpty(Data) Then
'If you want to operate directly on the array,
'you can leave out the following lines.
With Sheets(parSheetName)
'Delete any old content
' .Cells.ClearContents
'A range gets the same dimensions as the array
'and the array values are inserted in one operation.
.Cells(2, 1).Resize(UBound(Data, 1), UBound(Data, 2)) = Data
End With
End If
End Sub
'**************************************************************
Public Function isArrayEmpty(parArray As Variant) As Boolean
'Returns False if not an array or a dynamic array
'that hasn't been initialised (ReDim) or
'deleted (Erase).
If IsArray(parArray) = False Then isArrayEmpty = True
On Error Resume Next
If UBound(parArray) < LBound(parArray) Then
isArrayEmpty = True
Exit Function
Else
isArrayEmpty = False
End If
End Function
'**************************************************************
Private Function getDataFromFile(parFileName As String, _
parDelimiter As String, _
Optional parExcludeCharacter As String = "") As Variant
'parFileName is the delimited file (csv, txt ...)
'parDelimiter is the separator, e.g. semicolon.
'The function returns an empty array, if the file
'is empty or cannot be opened.
'Number of columns is based on the line with most
'columns and not the first line.
'parExcludeCharacter: Some csv files have strings in
'quotations marks ("ABC"), and if parExcludeCharacter = """"
'quotation marks are removed.
Dim locLinesList() As Variant 'Array
Dim locData As Variant 'Array
Dim i As Long 'Counter
Dim j As Long 'Counter
Dim locNumRows As Long 'Nb of rows
Dim locNumCols As Long 'Nb of columns
Dim fso As Variant 'File system object
Dim ts As Variant 'File variable
Const REDIM_STEP = 10000 'Constant
'If this fails you need to reference Microsoft Scripting Runtime.
'You select this in "Tools" (VBA editor menu).
Set fso = CreateObject("Scripting.FileSystemObject")
On Error GoTo error_open_file
'Sets ts = the file
Set ts = fso.OpenTextFile(parFileName)
On Error GoTo unhandled_error
'Initialise the array
ReDim locLinesList(1 To 1) As Variant
i = 0
'Loops through the file, counts the number of lines (rows)
'and finds the highest number of columns.
Do While Not ts.AtEndOfStream
'If the row number Mod 10000 = 0
'we redimension the array.
If i Mod REDIM_STEP = 0 Then
ReDim Preserve locLinesList _
(1 To UBound(locLinesList, 1) + REDIM_STEP) As Variant
End If
locLinesList(i + 1) = Split(ts.ReadLine, parDelimiter)
j = UBound(locLinesList(i + 1), 1) 'Nb of columns in present row
'If the number of columns is then highest so far.
'the new number is saved.
If locNumCols < j Then locNumCols = j
i = i + 1
Loop
ts.Close 'Close file
locNumRows = i
'If number of rows is zero
If locNumRows = 0 Then Exit Function
ReDim locData(1 To locNumRows, 1 To locNumCols + 1) As Variant
'Copies the file values into an array.
'If parExcludeCharacter has a value,
'the characters are removed.
If parExcludeCharacter <> "" Then
For i = 1 To locNumRows
For j = 0 To UBound(locLinesList(i), 1)
If Left(locLinesList(i)(j), 1) = parExcludeCharacter Then
If Right(locLinesList(i)(j), 1) = parExcludeCharacter Then
locLinesList(i)(j) = _
Mid(locLinesList(i)(j), 2, Len(locLinesList(i)(j)) - 2)
Else
locLinesList(i)(j) = _
Right(locLinesList(i)(j), Len(locLinesList(i)(j)) - 1)
End If
ElseIf Right(locLinesList(i)(j), 1) = parExcludeCharacter Then
locLinesList(i)(j) = _
Left(locLinesList(i)(j), Len(locLinesList(i)(j)) - 1)
End If
locData(i, j + 1) = locLinesList(i)(j)
Next j
Next i
Else
For i = 1 To locNumRows
For j = 0 To UBound(locLinesList(i), 1)
locData(i, j + 1) = locLinesList(i)(j)
Next j
Next i
End If
getDataFromFile = locData
Exit Function
error_open_file: 'Returns empty Variant
unhandled_error: 'Returns empty Variant
End Function
If you want to use the FileSystemObject to obtain your data, you will need to convert each date item in your Data variable into a "real date" before writing it to the worksheet.
As it is in your screenshot, it is a string and, as you have discovered, the conversion when writing to the worksheet is unreliable.
One way to convert it.
Dim V As Variant, i As Long
For i = LBound(Data) To UBound(Data)
V = Split(Data(i, 3), "/")
'test for proper date
If UBound(V) = 2 Then _
Data(i, 3) = DateSerial(V(2), V(1), V(0))
Next i
Might be easier to IMPORT the data rather than using the FSO
I have 3 informations on a row and I can have multiple row selected. So what I'm looking for is a way to split a first time each row into an array.
That's what I'm doing here.
line = Split(msg, ",")
Then I want to for every line to split info to obtain a matrix with first identifer the line and the second is the info
ReDim pro(Ubound(line),3)
For i = 0 To Ubound(line)
pro(i) = Split(ligne(i), "/")
Next
But It throw me a mismatch error so I don't know how to do it
for example :
I have this
msg1/1250/Description,msg2/1500/Description2,msg3,45656,Desctiption3
And finally have this :
pro(0,0) = msg1
pro(0,1) = 1250
pro (1,1) = 1500
etc ...
Thank you
Not optimal in any way, but it should give you a start:
Dim RowCount As Integer
Dim i As Integer
Dim j As Integer
Dim x As Variant
Dim y As Variant
Line = "msg1/1250/Description,msg2/1500/Description2,msg3/45656/Desctiption3"
RowCount = UBound(Split(Line, ",")) + 1
ReDim pro(RowCount, 3)
For Each x In Split(Line, ",")
j = 0
For Each y In Split(x, "/")
pro(i, j) = y
j = j + 1
Next y
i = i + 1
Next x
What you have initially as pro is called a "jagged array". You can use a "double-transpose" to transform it into a 2D array. But beware that it needs that all the "line arrays" be of the same size:
Function toMatrix(msg as string)
Dim line: line = Split(msg, ",")
ReDim pro(UBound(line))
Dim i As Long
For i = 0 To UBound(line)
pro(i) = Split(line(i), "/")
Next
' transform array of arrays into a 2D array.
toMatrix = Application.Transpose(Application.Transpose(pro))
End Function
Sub Test
Dim msg As String
msg = "msg1/1250/Description,msg2/1500/Description2,msg3/45656/Desctiption3"
Dim ar
ar = toMatrix(msg) ' ar is now a 2D array
End Sub
This is how I did it:
Option Explicit
Public Sub TestMe()
Dim strInput As String
Dim arrVals As Variant
Dim arrVar As Variant
Dim arrVar2 As Variant
Dim arrResult As Variant
Dim lngCount As Long: lngCount = 0
strInput = "msg1/1250/Description,msg2/1500/Description2,msg3/45656/Desctiption3"
arrVals = Split(strInput, ",")
ReDim arrResult(UBound(arrVals), 1)
For Each arrVar In arrVals
arrVar2 = Split(arrVar, "/")
arrResult(lngCount, 0) = arrVar2(0)
arrResult(lngCount, 1) = arrVar2(1)
lngCount = lngCount + 1
Next arrVar
End Sub
That's the result:
As far as I did not see that you need a DescriptionN I have skipped it.
I have a requirement where in, I need to use the auto filter to filter the data first and then am using the advanced filter to get the Unique values alone. But the advanced filter doesn't take the auto filtered value alone. How do I use them together?
Here goes my code,
Colmz = WorksheetFunction.Match("RSDate", Sheets("RS_Report").Rows(1), 0)
ActiveSheet.ListObjects("RS").Range.AutoFilter Field:=Colmz, Criteria1:="YES"
ActiveSheet.Range("B1:B65536").AdvancedFilter Action:=xlFilterCopy, CopyToRange:=Sheets("CSRS").Range("B14"), Unique:=True
Kindly correct me and share your suggestions. Thanks
I would stick the unique values in an array - it's faster and less likely to break -
sub uniquearray()
Colmz = WorksheetFunction.Match("RSDate", Sheets("RS_Report").Rows(1), 0)
ActiveSheet.ListObjects("RS").Range.AutoFilter Field:=Colmz, Criteria1:="YES"
Call creatary(curary, Sheets("RS_Report"), Letter(Sheets("RS_Report"), "RSDate")): Call eliminateDuplicate(curary): Call BuildArrayWithoutBlankstwo(curary): Call Alphabetically_SortArray(curary)
For Each cell In curary
'do what you need to do with the unique array list
Next cell
end sub
Function creatary(ary As Variant, sh As Worksheet, ltr As String)
Dim x, y, rng As Range
ReDim ary(0)
Set rng = sh.Range(ltr & "2:" & ltr & sh.Range("A1000000").End(xlUp).Row).SpecialCells(xlCellTypeVisible)
x = 0
For Each y In rng
If Not Application.IsError(y) Then
If Not IsNumeric(y) Then
ary(x) = y
End If
x = x + 1
ReDim Preserve ary(x)
End If
Next y
End Function
Function BuildArrayWithoutBlankstwo(ary As Variant)
Dim AryFromRange() As Variant, AryNoBlanks() As Variant
Dim Counter As Long, NoBlankSize As Long
'set references and initialize up-front
ReDim AryNoBlanks(0 To 0)
NoBlankSize = 0
'load the range into array
AryFromRange = ary
'loop through the array from the range, adding
'to the no-blank array as we go
For Counter = LBound(AryFromRange) To UBound(AryFromRange)
If ary(Counter) <> 0 Then
NoBlankSize = NoBlankSize + 1
AryNoBlanks(UBound(AryNoBlanks)) = ary(Counter)
ReDim Preserve AryNoBlanks(0 To UBound(AryNoBlanks) + 1)
End If
Next Counter
'remove that pesky empty array field at the end
If UBound(AryNoBlanks) > 0 Then
ReDim Preserve AryNoBlanks(0 To UBound(AryNoBlanks) - 1)
End If
'debug for reference
ary = AryNoBlanks
End Function
Function eliminateDuplicate(ary As Variant) As Variant
Dim aryNoDup(), dupArrIndex, i, dupBool, j
dupArrIndex = -1
For i = LBound(ary) To UBound(ary)
dupBool = False
For j = LBound(ary) To i
If ary(i) = ary(j) And Not i = j Then
dupBool = True
End If
Next j
If dupBool = False Then
dupArrIndex = dupArrIndex + 1
ReDim Preserve aryNoDup(dupArrIndex)
aryNoDup(dupArrIndex) = ary(i)
End If
Next i
ary = aryNoDup
End Function
Function Alphabetically_SortArray(ary)
Dim myArray As Variant
Dim x As Long, y As Long
Dim TempTxt1 As String
Dim TempTxt2 As String
myArray = ary
'Alphabetize Sheet Names in Array List
For x = LBound(myArray) To UBound(myArray)
For y = x To UBound(myArray)
If UCase(myArray(y)) < UCase(myArray(x)) Then
TempTxt1 = myArray(x)
TempTxt2 = myArray(y)
myArray(x) = TempTxt2
myArray(y) = TempTxt1
End If
Next y
Next x
ary = myArray
End Function
Function Letter(oSheet As Worksheet, name As String, Optional num As Integer)
If num = 0 Then num = 1
Letter = Application.Match(name, oSheet.Rows(num), 0)
Letter = Split(Cells(, Letter).Address, "$")(1)
End Function
I'm having a problem reading in mixed datatypes from a .csv datasource: Strings are returned as Null's when I have a column with mixed String/Numeric values. I have set IMEX=1 and changed the Registry entry TypeGuessRows from 8 to 0 (but even if I have mixed datatypes within the first 8 rows, strings are still coming through as Null). Also ImportMixedTypes=Text in the registry.
What am I missing?? Any ideas much appreciated.
Here's my connection string:
ConnString = "Provider=Microsoft.Jet.OLEDB.4.0;" _
& "Data Source=" & Folder & ";" _
& "Extended Properties='text;HDR=YES;FMT=CSVDelimited;IMEX=1';" _
& "Persist Security Info=False;"
Here is another code sample that does not use ADO, similar to what Fink posted, with a little more flexibility and error handling. Performance is not too bad (reads and parses a 20 MB csv file in less than 3 seconds on my machine).
Public Function getDataFromFile(parFileName As String, parDelimiter As String, Optional parExcludeCharacter As String = "") As Variant
'parFileName is supposed to be a delimited file (csv...)'
'Returns an empty array if file is empty or can't be opened
'number of columns based on the line with the largest number of columns, not on the first line'
'parExcludeCharacter: sometimes csv files have quotes around strings: "XXX" - if parExcludeCharacter = """" then quotes are removed'
Dim locLinesList() As Variant
Dim locData As Variant
Dim i As Long
Dim j As Long
Dim locNumRows As Long
Dim locNumCols As Long
Dim fso As New FileSystemObject
Dim ts As TextStream
Const REDIM_STEP = 10000
On Error GoTo error_open_file
Set ts = fso.OpenTextFile(parFileName)
On Error GoTo unhandled_error
'Counts the number of lines and finds the largest number of columns'
ReDim locLinesList(1 To 1) As Variant
i = 0
Do While Not ts.AtEndOfStream
If i Mod REDIM_STEP = 0 Then
ReDim Preserve locLinesList(1 To UBound(locLinesList, 1) + REDIM_STEP) As Variant
End If
locLinesList(i + 1) = Split(ts.ReadLine, parDelimiter)
j = UBound(locLinesList(i + 1), 1) 'number of columns'
If locNumCols < j Then locNumCols = j
i = i + 1
Loop
ts.Close
locNumRows = i
If locNumRows = 0 Then Exit Function 'Empty file'
ReDim locData(1 To locNumRows, 1 To locNumCols + 1) As Variant
'Copies the file into an array'
If parExcludeCharacter <> "" Then
For i = 1 To locNumRows
For j = 0 To UBound(locLinesList(i), 1)
If Left(locLinesList(i)(j), 1) = parExcludeCharacter Then
If Right(locLinesList(i)(j), 1) = parExcludeCharacter Then
locLinesList(i)(j) = Mid(locLinesList(i)(j), 2, Len(locLinesList(i)(j)) - 2) 'If locTempArray = "", Mid returns ""'
Else
locLinesList(i)(j) = Right(locLinesList(i)(j), Len(locLinesList(i)(j)) - 1)
End If
ElseIf Right(locLinesList(i)(j), 1) = parExcludeCharacter Then
locLinesList(i)(j) = Left(locLinesList(i)(j), Len(locLinesList(i)(j)) - 1)
End If
Next j
Next i
Else
For i = 1 To locNumRows
For j = 0 To UBound(locLinesList(i), 1)
locData(i, j + 1) = locLinesList(i)(j)
Next j
Next i
End If
getDataFromFile = locData
Exit Function
error_open_file: 'returns empty variant'
unhandled_error: 'returns empty variant'
End Function
Are you locked into reading the CSV with ADO? I always seem to run into problems trying to read textfiles with ADO like you are experiencing. I usually just give up on the ADO side and read the file directly with a text reader to get more control.
Public Sub TestIt()
Dim path As String
path = "C:\test.csv"
ReadText path
End Sub
Public Sub ReadText(path As String)
'requires reference to 'Microsoft Scripting Runtime' scrrun.dll OR use late binding
Const DELIM As String = ","
Dim fso As New Scripting.FileSystemObject
Dim text As Scripting.TextStream
Dim line As String
Dim vals() As String
Set text = fso.OpenTextFile(path, ForReading)
Do While Not text.AtEndOfStream
line = text.ReadLine
vals = Split(line, DELIM)
'do something with the values
Loop
text.Close
End Sub