-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathCSVClass.cls
393 lines (303 loc) · 12.5 KB
/
CSVClass.cls
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
END
Attribute VB_Name = "CSVClass"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
'@Folder("TableManager.DataBase")
Option Explicit
Private Const Module_Name As String = "CSVClass."
Implements I_DataBase
Public Function I_DataBase_ArrayFromDataBase(ByVal Parameters As String) As Variant
I_DataBase_ArrayFromDataBase = ArrayFromCSVfile(Parameters)
End Function
Public Sub I_DataBase_ArrayToDataBase(ByRef MyArray() As Variant, ByVal Parameters As String)
SaveAsCSV MyArray, Parameters
End Sub
Public Sub I_DataBase_OpenDataBase()
End Sub
Public Sub I_DataBase_CloseDataBase()
End Sub
Private Function ArrayFromCSVfile( _
ByVal FullFileName As String, _
Optional ByVal RowDelimiter As String = vbCrLf, _
Optional ByVal FieldDelimiter As String = ",", _
Optional ByVal RemoveQuotes As Boolean = True _
) As Variant
' https://stackoverflow.com/questions/12259595/load-csv-file-into-a-vba-array-rather-than-excel-sheet
' Assumes file name ends with ".csv"
' Load a file created by FileToArray into a 2-dimensional array
' The file name is specified by strName, and it is exected to exist
' in the user's temporary folder. This is a deliberate restriction:
' it's always faster to copy remote files to a local drive than to
' edit them across the network
' RemoveQuotes=TRUE strips out the double-quote marks (Char 34) that
' encapsulate strings in most csv files.
Const RoutineName As String = Module_Name & "ArrayFromCSVfile"
On Error GoTo ErrorHandler
Dim FSO As Scripting.FileSystemObject
Dim DataArray As Variant
Set FSO = New Scripting.FileSystemObject
If Not FSO.FileExists(FullFileName) Then ' raise an error?
Exit Function
End If
Application.StatusBar = "Reading the file... (" & FullFileName & ")"
If Not RemoveQuotes Then
DataArray = Join2d(FSO.OpenTextFile(FullFileName, ForReading).ReadAll, RowDelimiter, FieldDelimiter)
Application.StatusBar = "Reading the file... Done"
Else
' we have to do some allocation here...
Dim OneLine As String
OneLine = FSO.OpenTextFile(FullFileName, ForReading).ReadAll
Application.StatusBar = "Reading the file... Done"
Application.StatusBar = "Parsing the file..."
OneLine = Replace$(OneLine, Chr$(34) & RowDelimiter, RowDelimiter)
OneLine = Replace$(OneLine, RowDelimiter & Chr$(34), RowDelimiter)
OneLine = Replace$(OneLine, Chr$(34) & FieldDelimiter, FieldDelimiter)
OneLine = Replace$(OneLine, FieldDelimiter & Chr$(34), FieldDelimiter)
If Right$(OneLine, Len(OneLine)) = Chr$(34) Then
OneLine = Left$(OneLine, Len(OneLine) - 1)
End If
If Left$(OneLine, 1) = Chr$(34) Then
OneLine = Right$(OneLine, Len(OneLine) - 1)
End If
Application.StatusBar = "Parsing the file... Done"
DataArray = Split2d(OneLine, RowDelimiter, FieldDelimiter)
OneLine = vbNullString
End If
DecodeCommas DataArray
Application.StatusBar = False
Set FSO = Nothing
ArrayFromCSVfile = DataArray
Erase DataArray
'@Ignore LineLabelNotUsed
Done:
Exit Function
ErrorHandler:
RaiseError Err.Number, Err.Source, RoutineName, Err.Description
End Function
Private Sub DecodeCommas(ByRef Ary As Variant)
Dim I As Long
Dim J As Long
For I = LBound(Ary, 1) To UBound(Ary, 1)
For J = LBound(Ary, 2) To UBound(Ary, 2)
Ary(I, J) = Replace$(Ary(I, J), "<comma>", ",")
Next J
Next I
End Sub
Private Function Split2d(ByVal InputString As String, _
Optional ByVal RowDelimiter As String = vbCrLf, _
Optional ByVal FieldDelimiter As String = vbTab, _
Optional ByVal CoerceLowerBound As Long = 1 _
) As Variant
' https://stackoverflow.com/questions/12259595/load-csv-file-into-a-vba-array-rather-than-excel-sheet
' Split up a string into a 2-dimensional array.
' Works like VBA.Strings.Split, for a 2-dimensional array.
' Check your lower bounds on return: never assume that any array in
' VBA is zero-based, even if you've set Option Base 0
' If in doubt, coerce the lower bounds to 0 or 1 by setting
' CoerceLowerBound
' Note that the default delimiters are those inserted into the
' string returned by ADODB.Recordset.GetString
Const RoutineName As String = Module_Name & "Split2d"
On Error GoTo ErrorHandler
' Coding note: we're not doing any string-handling in VBA.Strings -
' allocating, deallocating and (especially!) concatenating are SLOW.
' We're using the VBA Join & Split functions ONLY. The VBA Join,
' Split, & Replace functions are linked directly to fast (by VBA
' standards) functions in the native Windows code. Feel free to
' optimise further by declaring and using the Kernel string functions
' if you want to.
' ** THIS CODE IS IN THE PUBLIC DOMAIN **
' Nigel Heffernan Excellerando.Blogspot.com
Dim I As Long
Dim J As Long
Dim I_Lower As Long
Dim J_Lower As Long
Dim FirstRow As Long
Dim LastRow As Long
Dim FirstColumn As Long
Dim LastColumn As Long
Dim ArrayOfRows As Variant
Dim OneRow As Variant
ArrayOfRows = Split(InputString, RowDelimiter)
FirstRow = LBound(ArrayOfRows)
LastRow = UBound(ArrayOfRows)
If Len(VBA.LenB(ArrayOfRows(LastRow))) <= 1 Then
' clip out empty last row: a common artifact in data
'loaded from files with a terminating row delimiter
LastRow = LastRow - 1
End If
I = FirstRow
OneRow = Split(ArrayOfRows(I), FieldDelimiter)
FirstColumn = LBound(OneRow)
LastColumn = UBound(OneRow)
If VBA.LenB(OneRow(LastColumn)) <= 0 Then
' ! potential error: first row with an empty last field...
LastColumn = LastColumn - 1
End If
I_Lower = CoerceLowerBound - FirstRow
J_Lower = CoerceLowerBound - FirstColumn
Dim DataArray() As Variant
ReDim DataArray(FirstRow + I_Lower To LastRow + I_Lower, FirstColumn + J_Lower To LastColumn + J_Lower)
' As we've got the first row already... populate it
' here, and start the main loop from lbound+1
For J = FirstColumn To LastColumn
DataArray(FirstRow + I_Lower, J + J_Lower) = OneRow(J)
Next J
For I = FirstRow + I_Lower To LastRow + I_Lower - 1
OneRow = Split(ArrayOfRows(I), FieldDelimiter)
For J = LBound(OneRow, 1) To UBound(OneRow, 1)
DataArray(I + I_Lower, J + J_Lower) = OneRow(J)
Next J
Erase OneRow
Next I
Erase ArrayOfRows
Application.StatusBar = False
Split2d = DataArray
'@Ignore LineLabelNotUsed
Done:
Exit Function
ErrorHandler:
RaiseError Err.Number, Err.Source, RoutineName, Err.Description
End Function
Private Function Join2d( _
ByVal InputArray As Variant, _
Optional ByVal RowDelimiter As String = vbCr, _
Optional ByVal FieldDelimiter As String = vbTab, _
Optional ByVal SkipBlankRows As Boolean = False _
) As String
' https://stackoverflow.com/questions/12259595/load-csv-file-into-a-vba-array-rather-than-excel-sheet
' Join up a 2-dimensional array into a string. Works like the standard
' VBA.Strings.Join, for a 2-dimensional array.
' Note that the default delimiters are those inserted into the string
' returned by ADODB.Recordset.GetString
Const RoutineName As String = Module_Name & "Join2d"
On Error GoTo ErrorHandler
' Coding note: we're not doing any string-handling in VBA.Strings -
' allocating, deallocating and (especially!) concatenating are SLOW.
' We're using the VBA Join & Split functions ONLY. The VBA Join,
' Split, & Replace functions are linked directly to fast (by VBA
' standards) functions in the native Windows code. Feel free to
' optimise further by declaring and using the Kernel string functions
' if you want to.
' ** THIS CODE IS IN THE PUBLIC DOMAIN **
' Nigel Heffernan Excellerando.Blogspot.com
Dim I As Long
Dim J As Long
Dim I_LBound As Long
Dim I_UBound As Long
Dim J_LBound As Long
Dim J_UBound As Long
Dim ArrayOfRows() As String
Dim ArrayOfColumns() As String
Dim BlankRow As String
I_LBound = LBound(InputArray, 1)
I_UBound = UBound(InputArray, 1)
J_LBound = LBound(InputArray, 2)
J_UBound = UBound(InputArray, 2)
ReDim ArrayOfRows(I_LBound To I_UBound)
ReDim ArrayOfColumns(J_LBound To J_UBound)
For I = I_LBound To I_UBound
For J = J_LBound To J_UBound
ArrayOfColumns(J) = InputArray(I, J)
Next J
ArrayOfRows(I) = Join(ArrayOfColumns, FieldDelimiter)
Next I
If SkipBlankRows Then
If Len(FieldDelimiter) = 1 Then
BlankRow = String(J_UBound - J_LBound, FieldDelimiter)
Else
For J = J_LBound To J_UBound
BlankRow = BlankRow & FieldDelimiter
Next J
End If
Join2d = Replace(Join(ArrayOfRows, RowDelimiter), BlankRow, RowDelimiter, vbNullString)
I = Len(BlankRow & RowDelimiter)
If Left$(Join2d, I) = BlankRow & RowDelimiter Then
Mid$(Join2d, 1, I) = vbNullString
End If
Else
Join2d = Join(ArrayOfRows, RowDelimiter)
End If
Erase ArrayOfRows
'@Ignore LineLabelNotUsed
Done:
Exit Function
ErrorHandler:
RaiseError Err.Number, Err.Source, RoutineName, Err.Description
End Function
Private Sub SaveAsCSV( _
ByRef MyArray() As Variant, _
ByVal Filename As String, _
Optional ByVal LowerBound As Long = 1, _
Optional ByVal Delimiter As String = ",")
' https://stackoverflow.com/questions/4191560/create-csv-from-array-in-vba
' SaveAsCSV saves an array as csv file. Choosing a delimiter different as a comma, is optional.
'
' Syntax:
' SaveAsCSV dMyArray, sMyFileName, [sMyDelimiter]
'
' Examples:
' SaveAsCSV dChrom, app.path & "\Demo.csv" --> comma as delimiter
' SaveAsCSV dChrom, app.path & "\Demo.csv", ";" --> semicolon as delimiter
'
' Rev. 1.00 [8 jan 2003]
' written by P. Wester
Dim I As Long 'counter
Dim J As Long 'counter
Dim OneRow As String 'csv string to print
Const RoutineName As String = Module_Name & "SaveACSV"
On Error GoTo ErrorHandler
Dim UpperBound1 As Long
Dim UpperBound2 As Long
Dim PrintString As String
If NumberOfArrayDimensions(MyArray()) = 1 Then '1 dimensional
Open Filename For Output As #7
' TODO Check the default value of lower bound of one-dimensional array
If LowerBound = 1 Then
UpperBound1 = UBound(MyArray(), 1)
Else
UpperBound1 = UBound(MyArray(), 1) - 1
End If
For I = LowerBound To UpperBound1
' Print #7, Format$(MyArray(I, 0), "0.000000E+00")
PrintString = Replace$(MyArray(I, 0), ",", "<comma>")
Print #7, PrintString
Next I
Close #7
ElseIf NumberOfArrayDimensions(MyArray()) = 2 Then '2 dimensional
Open Filename For Output As #7
If LowerBound = 1 Then
UpperBound1 = UBound(MyArray(), 1)
UpperBound2 = UBound(MyArray(), 2)
Else
UpperBound1 = UBound(MyArray(), 1) - 1
UpperBound2 = UBound(MyArray(), 2) - 1
End If
For I = LowerBound To UpperBound1
OneRow = vbNullString
For J = LowerBound To UpperBound2
PrintString = Replace$(MyArray(I, J), ",", "<comma>")
OneRow = OneRow & PrintString & Delimiter
Next J
OneRow = Left$(OneRow, Len(OneRow) - 1) 'remove last Delimiter
Print #7, OneRow
Next I
Close #7
Else
' Error.Raise ArrayMustBe1or2Dimensions, RoutineName, "Array must be 1 or 2 dimensions"
' RaiseError Err.Number, Err.Source, RoutineName, Err.Description
RaiseError ArrayMustBe1or2Dimensions, Err.Source, RoutineName, "Array must be 1 or 2 dimensions"
End If
'@Ignore LineLabelNotUsed
Done:
Exit Sub
ErrorHandler:
Close #7
RaiseError Err.Number, Err.Source, RoutineName, Err.Description
End Sub