VBA read CSV with delimiter in string - csv

I'm trying to read a .csv to work with it in an .accdb
The file has ; as delimiter and "" as string qualifier.
Young and naive as I was I just split the file at the delimiter:
Set oFSO = New FileSystemObject
Set oStream = oFSO.OpenTextFile(sFilePath, ForReading)
Do Until oStream.AtEndOfStream
sLine = oStream.ReadLine
sArray = Split(sLine, ";")
....
Now I got a line that reads:
"String";"Str;ing";0;0;0;"String"
So I have delimiter inside one of the strings which makes the code above not work. Any ideas how to solve this?
EDIT:
I've found someone with a similar problem, only with a comma as delimiter. And they solved it using regular expressions.
The problem: I'm absolutely not good with regular expressions. In the example the used this expression and code:
Function regLine(sLine As String) As String
Dim oRegEx As RegExp
Set oRegEx = New RegExp
oRegEx.IgnoreCase = True
oRegEx.Global = True
' Pattern: ",(?=([^"]*"[^"]*")*(?![^"]*"))"
oRegEx.Pattern = ",(?=([^" & Chr(34) & "]*" & Chr(34) & "[^" & Chr(34) & "]*" & Chr(34) & ")*(?![^" & Chr(34) & "]*" & Chr(34) & "))"
regLine = oRegEx.Replace(sLine, ";")
End Function
So I don't really understand the expression. My first idea was to replace the comma with a semicolon but that didn't work.

Option Explicit
Dim line
line ="""String"";""Str;ing"";0;0;0;""String"""
WScript.Echo line
Dim aFields
With New RegExp
.Pattern = "(""[^""]*"")?;"
.Global = True
aFields = Split(.Replace(line, "$1"&Chr(0)),Chr(0))
End With
Dim field
For Each field In aFields
WScript.Echo field
Next
Code is .vbs, but shows how to use the regular expression to replace semicolons not enclosed in quotes with a null character and use the null character to split the line into its fields.

I solved the problem now by writing a loop, that deletes the delimiter if it is in a string.
Function fixLine(sLine As String)
Dim i As Long
Dim bInString As Boolean
bInString = False
fixLine = ""
For i = 1 To Len(sLine)
If Mid(sLine, i, 1) = Chr(34) Then
If bInString Then
bInString = False
Else
bInString = True
End If
End If
If bInString And Mid(sLine, i, 1) = ";" Then
Else
fixLine = fixLine & Mid(sLine, i, 1)
End If
Next
End Function
It kind of feels quick and dirty and I'm not sure about the performance but it works.
EDIT:
I also worked with theabove example I found. It replaces the delimiter in a line outside of strings. So I replaced the delimiter with Chr(0) which I know won't apear in a line and then split at the new delimiter.
Function regLine(sLine As String) As String()
Dim oRegEx As RegExp
Dim sLine2() As String
Set oRegEx = New RegExp
oRegEx.Global = True
'Pattern: ";(?=([^"]*"[^"]*")*(?![^"]*"))"
oRegEx.Pattern = ";(?=([^" & Chr(34) & "]*" & Chr(34) & "[^" & Chr(34) & "]*" & Chr(34) & ")*(?![^" & Chr(34) & "]*" & Chr(34) & "))"
sLine2 = oRegEx.Replace(sLine, Chr(0))
regLine = Split(sLine2, Chr(0))
End Function

My first question is: Is there any case where a ";" in the string values is a valid string? If so, I don't see any way other than manually verifying the data.
If not, how large is the input file? If it's not too big (for various definitions of "too" :-) ) then just manually scan it for errors.
If it is very large, I'd simple write a preprocesser program that reads the string values then deletes any ";" in those where it occurs. Such a program is only about a dozen lines long. Then run the clean file into Access.

Related

VBA returning a value and error information together

I am writing some VBA in MS Access, although the principle of my question would apply just as well to Excel or Word VBA. I have written a function GetStringParameterFromTable which returns a string value. It is possible that the function may result in a VBA-generated error, despite my best efforts to write it so that it does not. If an error happens, I don't want the code to crash, so I must use error handling. However, I don't want the code to display an error message and stop within the function if there is an error. I want the function to finish executing and return control to the calling procedure, and then I want the calling procedure to display the error message and tidy up, e.g. close open files. My question is: how does the calling procedure know that there has been an error in the function it called, and how does it get the error message?
I have thought of three ways of implementing this:
(1) Make GetStringParameterFromTable into a Sub, and pass it ParameterValue, ErrorFlag and ErrorMessage by reference.
(2) Keep GetStringParameterFromTable as a Function, define ErrorFlag and ErrorMessage as global variables and have the function alter ErrorFlag and ErrorMessage.
(3) Keep GetStringParameterFromTable as a Function and define a type with three components – ParameterValue, ErrorFlag and ErrorMessage – and make GetStringParameterFromTable return a value of the type I have defined.
I think that my requirement must be quite common, but I can’t find any examples of how it’s implemented. Does anyone have any views on which of my suggestions is the best way, or whether there is a better way that I haven’t thought of?
I have been contemplating the same thing since C#.net has implemented Tuples. I have implemented Tuples using VBA's type to create my tuples. What I have done is the following:
Public Type myTuple
Value as String 'Or whatever type your value needs to be
ErrCode as Long
ErrDesc as String
End Type
Public Function DoWork (ByRef mObject as MyClass) as myTuple
Dim retVal as myTuple
'Do whatever work
If Err.Number <> 0 then
retVal.Value = Nothing
retVal.ErrNumber = Err.Number
retVal.ErrDesc = Err.Description
Else
Set retVal.Value = Whatever Makes Sense
retVal.ErrNumber = 0
retVal.ErrDesc = VbNullString
End If
DoWork = retVal
End Function
I would like to be more specific, but you didn't provide a code example.
I am doing it like this and log the errors in a table:
' Lookups Replacements
'---------------------
Function DLook(Expression As String, Domain As String, Optional Criteria) As Variant
On Error GoTo Err_Handler
Dim strSQL As String
strSQL = "SELECT " & Expression & " FROM " & Domain 'DLookup
'DCount: strSQL = "SELECT COUNT(" & Expression & ") FROM " & Domain
'DMax: strSQL = "SELECT MAX(" & Expression & ") FROM " & Domain
'DMin: strSQL = "SELECT SUM(" & Expression & ") FROM " & Domain
'DFirst: strSQL = "SELECT FIRST(" & Expression & ") FROM " & Domain
'DLast: strSQL = "SELECT LAST(" & Expression & ") FROM " & Domain
'DSum: strSQL = "SELECT SUM(" & Expression & ") FROM " & Domain
'DAvg: strSQL = "SELECT AVG(" & Expression & ") FROM " & Domain
If Not IsMissing(Criteria) Then strSQL = strSQL & " WHERE " & Criteria
DLook = DBEngine(0)(0).OpenRecordset(strSQL, dbOpenForwardOnly)(0)
Exit Function
Err_Handler:
'Can be made as Error Sub as well
Dim ErrNumber as Integer
Dim ErrDescription as String
ErrNumber = Err.Number
ErrDescription = Err.Description
Err.Clear
On Error Resume Next
Dim strSQL as String
strSQL = "INSERT INTO tblErrorLog (ErrorNumber, ErrorDescription) VALUES (" & ErrNumber & ", '" & ErrDescription & "')"
Currentdb.Excecute strSQL, dbFailOnError
End Function
Called with:
If DLook("Column2", "Table1", "Column1 = " & ID) = 0 Then
'Do stuff
End If
If DLook("Column2", "Table1") = 0 Then
'Do other stuff
End If

Output ADODB.RecordSet as JSON

I'm trying to change my application so that it outputs JSON instead of HTML when it makes an AJAX request for some data. I have an ADODB RecordSet. I need to loop through it row-by-row and add/change/remove different values. Then I need to take all the modified rows and response.write them as JSON. I'm using JSON2.asp so my application already supports JSON.parse & JSON.stringify but I can't get it to spit out the multi-dimensional array as JSON.
set rs = conn.execute(strQuery)
if Not rs.EOF Then
rsArray = rs.GetRows() 'This pulls in all the results of the RecordSet as a 2-dimensional array
columnCount = ubound(rsArray,1)
rowCount = ubound(rsArray,2)
For rowIndex = 0 to rowCount 'Loop through rows as the outer loop
rsArray(3,0) = "somethingelse"
Next 'Move on to next row if there is one
response.write JSON.stringify(rsArray) & " _______ "
End If
I just need to be able to go through the results of my database query, modify the results, and then output the modified results in JSON format. What's the right way to do this?
The JSON2.asp implementation doesn't have a "Load From Database" function which means you will have to implement something to convert the ADODB.Recordset to a JSON structure yourself.
If you are willing to use a different script there is an implementation by RCDMK on GitHub that does have a LoadRecordset() method, it's called JSON object class 3.5.3.
This makes loading data from an ADODB.Recordset really straightforward.
<!-- #include virtual="/jsonObject.class.asp" -->
<%
Response.LCID = 2057
'...
Dim rs: Set rs = conn.execute(strQuery)
Dim JSON: Set JSON = New JSONobject
Call JSON.LoadRecordset(rs)
Call Response.Clear()
Response.ContentType = "application/json"
Call JSON.Write()
%>
Code has been tested using a disconnected recordset, the ... here denote assumed code to setup your recordset, connection etc
It's worth noting you could write this yourself, it's not a huge leap to loop through an ADODB.Recordset and build a JSON string. However, I would argue against for a few reasons;
It is a time-consuming exercise.
Very easy to miss something (like checking for numeric data types, when generating output).
Depending on how it is coded can make it awkward to maintain (For example, if not injecting property names directly from the recordset and choosing to "hardcode" them instead).
Why reinvent the wheel ? There are a lot of public implementations in the wild that deal with the issues raised here. Admittedly, some are better than others, but it takes five minutes to include them and give it a try.
Just for completeness here is my local test code using a disconnected recordset
<!-- #include virtual="/jsonObject.class.asp" -->
<%
Call init()
Sub init()
Dim fields: fields = Array(Array("title", adVarChar, 50), Array("firstname", adVarChar, 50), Array("lastname", adVarChar, 50), Array("age", adInteger, 4))
Dim rs: Set rs = Server.CreateObject("ADODB.Recordset")
Call InsertRow(rs, fields, Array("Mr", "Joe", "Bloggs", 31))
Call InsertRow(rs, fields, Array("Mr", "John", "Smith", 42))
Response.LCID = 2057
Dim JSON: Set JSON = New JSONobject
Call JSON.LoadRecordset(rs)
Call Response.Clear()
Response.ContentType = "application/json"
Call JSON.Write()
End Sub
Sub InsertRow(ByVal rs, fields, values)
With rs
If rs.State <> adStateOpen Then
For Each fld In fields
Call .Fields.Append(fld(0), fld(1), fld(2))
Next
.CursorLocation = adUseClient
.CursorType = adOpenDynamic
Call .Open()
End If
Call .AddNew()
For i = 0 To UBound(fields, 1)
.Fields(fields(i)(0)).Value = values(i)
Next
Call .Update()
Call .MoveFirst()
End With
End Sub
%>
Output:
{"data":[{"title":"Mr","firstname":"Joe","lastname":"Bloggs","age":31},{"title":"Mr","firstname":"John","lastname":"Smith","age":42}]}
Here ya go. This works for me.
set rs = conn.execute(strQuery)
c=0
Response.write "["
Do Until rs.eof
'Assign variables here with whatever you need to change
title = rs(0)
fName = rs(1)
lName = rs(2)
empID = rs(3)
With Response
if c > 0 then .write ", "
.write "{" & chr(34) & "Title" & chr(34) & " : " & chr(34) & title & chr(34) & ", " & chr(34) & "FirstName" & chr(34) & " : " & chr(34) & fName & chr(34) & ", "
.write chr(34) & "LastName" & chr(34) & " : " & chr(34) & lName & chr(34) & ", " & chr(34) & "EmpID" & chr(34) & " : " & chr(34) & empID & chr(34) & "}"
End With
c = c + 1
rs.MoveNext
Loop
Response.write "]"
This will write your JSON object directly to the page.
try setting content-type to "application/json" on top of your asp page.
<%#LANGUAGE="VBSCRIPT" CODEPAGE="65001"%>
<%
Option Explicit
Response.Buffer=True
Response.ContentType="application/json"
Response.Charset="utf-8"
'' rest of your code.. your db operations
'' response write your json
%>

Access VBA Filtering code Syntax Error issue

I have the code below (courtesy of allenbrowne, his info are in the code). When I am filtering using one of the columns (One_or_Two_Pearl) it is giving me a (Syntax error missing operator in query expression). I can not trace the problem as everything look fine. That column is defined as Text type and contains data (1, 2, NA). This problem is only occurring for this column, and when I debug the yellow mark is indicating the (Me.Filter = strWhere) part.
Thank you in advance for your help.
'http://allenbrowne.com/ser-62.html
'Purpose: This module illustrates how to create a search form, _
where the user can enter as many or few criteria as they wish, _
and results are shown one per line.
'Note: Only records matching ALL of the criteria are returned.
'Author: Allen Browne (allen#allenbrowne.com), June 2006.
Option Compare Database
Option Explicit
Private Sub cmdFilter_Click()
'Purpose: Build up the criteria string form the non-blank search boxes, and apply to the form's Filter.
'Notes: 1. We tack " AND " on the end of each condition so you can easily add more search boxes; _
we remove the trailing " AND " at the end.
' 2. The date range works like this: _
Both dates = only dates between (both inclusive. _
Start date only = all dates from this one onwards; _
End date only = all dates up to (and including this one).
Dim strWhere As String 'The criteria string.
Dim lngLen As Long 'Length of the criteria string to append to.
Const conJetDate = "\#mm\/dd\/yyyy\#" 'The format expected for dates in a JET query string.
'***********************************************************************
'Look at each search box, and build up the criteria string from the non-blank ones.
'***********************************************************************
'Text field example. Use quotes around the value in the string.
If Not IsNull(Me.cboxprojphase) Then
strWhere = strWhere & "([Project_Phase] = """ & Me.cboxprojphase & """) AND "
End If
'Another text field example. Use Like to find anywhere in the field.
If Not IsNull(Me.cboxcontract) Then
strWhere = strWhere & "([Contract] = """ & Me.cboxcontract & """) AND "
End If
'Number field example. Do not add the extra quotes.
If Not IsNull(Me.cboxdesigndpm) Then
strWhere = strWhere & "([Design_DPM] = """ & Me.cboxdesigndpm & """) AND "
End If
'Text field example. Use quotes around the value in the string.
If Not IsNull(Me.cboxadmupc) Then
strWhere = strWhere & "([ADM/UPC] = """ & Me.cboxadmupc & """) AND "
End If
'Text field example. Use quotes around the value in the string.
If Not IsNull(Me.cboxpearl) Then
strWhere = strWhere & "([One_or_Two_Pearl] = """ & Me.cboxpearl & """) "
End If
'***********************************************************************
'Chop off the trailing " AND ", and use the string as the form's Filter.
'***********************************************************************
'See if the string has more than 5 characters (a trailng " AND ") to remove.
lngLen = Len(strWhere) - 5
If lngLen <= 0 Then 'Nah: there was nothing in the string.
MsgBox "No criteria", vbInformation, "Nothing to do."
Else 'Yep: there is something there, so remove the " AND " at the end.
strWhere = Left$(strWhere, lngLen)
'For debugging, remove the leading quote on the next line. Prints to Immediate Window (Ctrl+G).
'Debug.Print strWhere
'Finally, apply the string as the form's Filter.
Me.Filter = strWhere
Me.FilterOn = True
End If
End Sub
Private Sub cmdReset_Click()
'Purpose: Clear all the search boxes in the Form Header, and show all records again.
Dim ctl As Control
'Clear all the controls in the Form Header section.
For Each ctl In Me.Section(acHeader).Controls
Select Case ctl.ControlType
Case acTextBox, acComboBox
ctl.Value = Null
Case acCheckBox
ctl.Value = False
End Select
Next
'Remove the form's filter.
Me.FilterOn = False
Me.OrderByOn = False
End Sub
Private Sub Form_BeforeInsert(Cancel As Integer)
'To avoid problems if the filter returns no records, we did not set its AllowAdditions to No.
'We prevent new records by cancelling the form's BeforeInsert event instead.
'The problems are explained at http://allenbrowne.com/bug-06.html
Cancel = True
MsgBox "You cannot add new Records to the search form.", vbInformation, "Permission denied."
End Sub
Private Sub Form_Open(Cancel As Integer)
'Remove the single quote from these lines if you want to initially show no records.
'Me.Filter = "(False)"
'Me.FilterOn = True
Dim strURL As String
Dim objIE As Object
Dim arrSites(2) As String
Dim i As Integer
arrSites(0) = "http://google.com"
arrSites(1) = "http://google.com"
Set objIE = CreateObject("InternetExplorer.Application")
For i = 0 To 1 Step 1
strURL = arrSites(i)
If i = 0 Then
objIE.Navigate strURL
Else
objIE.Navigate2 strURL, 2048
End If
Next i
objIE.Visible = True
Set objIE = Nothing
'objIE.Quit
End Sub
Private Sub optSortorder_AfterUpdate()
If Me.optSortorder = 1 Then
Me.OrderBy = Me.cboSortField
Else
Me.OrderBy = Me.cboSortField & " DESC"
End If
Me.OrderByOn = True
End Sub
Both your problems are here:
strWhere = Left$(strWhere, lngLen)
'For debugging, remove the leading quote on the next line. Prints to Immediate Window (Ctrl+G).
'Debug.Print strWhere
Uncomment the Debug.Print line to see what is happening:
it tries to remove a trailing " AND " from your [One_or_Two_Pearl] line, but there is none.

Is this function putting a space in?

We have a site which has a number of useful functions written by our third-party programmer, but lately, I've noticed that one of them seems to be putting a space in when it runs, but I can't seem to find where that might be in order to remove it.
The function is called "formatspecialcharacters". It's function is to take a string and look through it to change special characters from the string into HTML entities and is written as:
function formatspecialcharacters(stringtoformat)
formatspecialcharacters = ""
if isblank(stringtoformat) then exit function
stringtoformat = CStr(stringtoformat)
stringtoformat = Trim(stringtoformat)
fieldcontents = HTMLDecode(stringtoformat)
if Len(fieldcontents)>0 then
for character_i = 1 to Len(fieldcontents)
character_c = asc(mid(fieldcontents, character_i, 1))
select case character_c
case 174, 169
formatspecialcharacters = formatspecialcharacters & "<sup>" & chr(character_c) & "</sup>"
case else
formatspecialcharacters = formatspecialcharacters & chr(character_c)
end select
next
end if
end function
The other function running inside the one above (HTMLDecode) is written as:
Function HTMLDecode(sText)
sText = vbcrlf & vbtab & sText
Dim I
sText = Replace(sText, """, Chr(34))
sText = Replace(sText, "<" , Chr(60))
sText = Replace(sText, ">" , Chr(62))
sText = Replace(sText, Chr(62) , Chr(62) & vbcrlf & vbtab)
sText = Replace(sText, "&" , Chr(38))
sText = Replace(sText, " ", Chr(32))
sText = Replace(sText, Chr(147), Chr(34)) 'smart quotes to proper quotes
sText = Replace(sText, Chr(148), Chr(34))
sText = Replace(sText, Chr(146), Chr(39)) 'smart apostrophe to proper apostrophe
For I = 1 to 255
sText = Replace(sText, "&#" & I & ";", Chr(I))
Next
HTMLDecode = sText
End Function
I think it's probably in the second function because when I use it like this:
<%=formatspecialcharacters(decendentdocumentformat_label(j))%>
Where "decendentdocumentformat_filename(j)" = "/example.html" and "formatspecialcharacters(decendentdocumentformat_label(j))" = "Web Page"
In this example, when it's rendered, I have the link, followed by a space and then the label (in this case, "Web Page") when it should just be the link then the label with no space between them.
Any help would be great.
Thanks in advance.
Not 100% sure I follow but if you were to;
<p><%=formatspecialcharacters("AAA") %><%=formatspecialcharacters("BBB") %></p>
You would see a space; AAA BBB because the 1st thing HTMLDecode does is prepend a carriage-return/line feed & tab to the input string, which the browser displays as a whitespace.
If you dont want the visible space remove sText = vbcrlf & vbtab & sText
(Also, the input is not trimmed after HTMLDecode, so if it were passed "XXX " you would have a trailing space)

Escaping non-ASCII characters (or how to remove the BOM?)

I need to create an ANSI text file from an Access recordset that outputs to JSON and YAML. I can write the file, but the output is coming out with the original characters, and I need to escape them. For example, an umlaut-O (ö) should be "\u00f6".
I thought encoding the file as UTF-8 would work, but it doesn't. However, having looked at the file coding again, if you write "UTF-8 without BOM" then everything works.
Does anyone know how to either
a) Write text out as UTF-8 without BOM, or
b) Write in ANSI but escaping the non-ASCII characters?
Public Sub testoutput()
Set db = CurrentDb()
str_filename = "anothertest.json"
MyFile = CurrentProject.Path & "\" & str_filename
str_temp = "Hello world here is an ö"
fnum = FreeFile
Open MyFile For Output As fnum
Print #fnum, str_temp
Close #fnum
End Sub
... ok .... i found some example code on how to remove the BOM. I would have thought it would be possible to do this more elegantly when actually writing the text in the first place. Never mind. The following code removes the BOM.
(This was originally posted by Simon Pedersen at http://www.imagemagick.org/discourse-server/viewtopic.php?f=8&t=12705)
' Removes the Byte Order Mark - BOM from a text file with UTF-8 encoding
' The BOM defines that the file was stored with an UTF-8 encoding.
Public Function RemoveBOM(filePath)
' Create a reader and a writer
Dim writer, reader, fileSize
Set writer = CreateObject("Adodb.Stream")
Set reader = CreateObject("Adodb.Stream")
' Load from the text file we just wrote
reader.Open
reader.LoadFromFile filePath
' Copy all data from reader to writer, except the BOM
writer.Mode = 3
writer.Type = 1
writer.Open
reader.Position = 5
reader.CopyTo writer, -1
' Overwrite file
writer.SaveToFile filePath, 2
' Return file name
RemoveBOM = filePath
' Kill objects
Set writer = Nothing
Set reader = Nothing
End Function
It might be useful for someone else.
Late to the game here, but I can't be the only coder who got got fed up with my SQL imports being broken by text files with a Byte Order Marker. There are very few 'Stack questions that touch on the problem - this is one of closest - so I'm posting an overlapping answer here.
I say 'overlapping' because the code below is solving a slightly different problem to yours - the primary purpose is writing a Schema file for a folder with a heterogeneous collection of files - but the BOM-handling segment is clearly marked.
The key functionality is that we iterate through all the '.csv' files in a folder, and we test each file with a quick nibble of the first four bytes: and we only only strip out the Byte Order Marker if we see one.
After that, we're working in low-level file-handling code from the primordial C. We have to, all the way down to using byte arrays, because everything else that you do in VBA will deposit the Byte Order Markers embedded in the structure of a string variable.
So, without further adodb, here's the code:
BOM-Disposal code for text files in a schema.ini file:
Public Sub SetSchema(strFolder As String)
On Error Resume Next
' Write a Schema.ini file to the data folder.
' This is necessary if we do not have the registry privileges to set the
' correct 'ImportMixedTypes=Text' registry value, which overrides IMEX=1
' The code also checks for ANSI or UTF-8 and UTF-16 files, and applies a
' usable setting for CharacterSet ( UNICODE|ANSI ) with a horrible hack.
' OEM codepage-defined text is not supported: further coding is required
' ...And we strip out Byte Order Markers, if we see them - the OLEDB SQL
' provider for textfiles can't deal with a BOM in a UTF-16 or UTF-8 file
' Not implemented: handling tab-delimited files or other delimiters. The
' code assumes a header row with columns, specifies 'scan all rows', and
' imposes 'read the column as text' if the data types are mixed.
Dim strSchema As String
Dim strFile As String
Dim hndFile As Long
Dim arrFile() As Byte
Dim arrBytes(0 To 4) As Byte
If Right(strFolder, 1) <> "\" Then strFolder = strFolder & "\"
' Dir() is an iterator function when you call it with a wildcard:
strFile = VBA.FileSystem.Dir(strFolder & "*.csv")
Do While Len(strFile) > 0
hndFile = FreeFile
Open strFolder & strFile For Binary As #hndFile
Get #hndFile, , arrBytes
Close #hndFile
strSchema = strSchema & "[" & strFile & "]" & vbCrLf
strSchema = strSchema & "Format=CSVDelimited" & vbCrLf
strSchema = strSchema & "ImportMixedTypes=Text" & vbCrLf
strSchema = strSchema & "MaxScanRows=0" & vbCrLf
If arrBytes(2) = 0 Or arrBytes(3) = 0 Then ' this is a hack
strSchema = strSchema & "CharacterSet=UNICODE" & vbCrLf
Else
strSchema = strSchema & "CharacterSet=ANSI" & vbCrLf
End If
strSchema = strSchema & "ColNameHeader = True" & vbCrLf
strSchema = strSchema & vbCrLf
' BOM disposal - Byte order marks confuse OLEDB text drivers:
If arrBytes(0) = &HFE And arrBytes(1) = &HFF _
Or arrBytes(0) = &HFF And arrBytes(1) = &HFE Then
hndFile = FreeFile
Open strFolder & strFile For Binary As #hndFile
ReDim arrFile(0 To LOF(hndFile) - 1)
Get #hndFile, , arrFile
Close #hndFile
BigReplace arrFile, arrBytes(0) & arrBytes(1), ""
hndFile = FreeFile
Open strFolder & strFile For Binary As #hndFile
Put #hndFile, , arrFile
Close #hndFile
Erase arrFile
ElseIf arrBytes(0) = &HEF And arrBytes(1) = &HBB And arrBytes(2) = &HBF Then
hndFile = FreeFile
Open strFolder & strFile For Binary As #hndFile
ReDim arrFile(0 To LOF(hndFile) - 1)
Get #hndFile, , arrFile
Close #hndFile
BigReplace arrFile, arrBytes(0) & arrBytes(1) & arrBytes(2), ""
hndFile = FreeFile
Open strFolder & strFile For Binary As #hndFile
Put #hndFile, , arrFile
Close #hndFile
Erase arrFile
End If
strFile = ""
strFile = Dir
Loop
If Len(strSchema) > 0 Then
strFile = strFolder & "Schema.ini"
hndFile = FreeFile
Open strFile For Binary As #hndFile
Put #hndFile, , strSchema
Close #hndFile
End If
End Sub
Public Sub BigReplace(ByRef arrBytes() As Byte, ByRef SearchFor As String, ByRef ReplaceWith As String)
On Error Resume Next
Dim varSplit As Variant
varSplit = Split(arrBytes, SearchFor)
arrBytes = Join$(varSplit, ReplaceWith)
Erase varSplit
End Sub
The code's easier to understand if you know that a Byte Array can be assigned to a VBA.String, and vice versa. The BigReplace() function is a hack that sidesteps some of VBA's inefficient string-handling, especially allocation: you'll find that large files cause serious memory and performance problems if you do it any other way.