I have to insert commas at certain points in lines of text, using VBScript. I need it to check the first four charcters of each line with an if statement and if it matches it inserts the commas required for that line this is what I have so far:
Const ForReading = 1
Const ForWriting = 2
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile("H:\Letter Display\Letters\LTRPRT__00000008720000000001NI-K-RMND.txt", ForReading)
strNIK = "1000"
strLine = objFile.ReadLine
If Left(strLine,4) = strNIK then
arrCommas = Array(16,31,46,56,66,79,94,99)
Do Until objFile.AtEndOfStream
intLength = Len(strLine)
For Each strComma in arrCommas
strLine = Left(strLine, strComma - 1) + "," + Mid(strLine, strComma, intLength)
Next
strText = strText & strLine & vbCrLf
Loop
end if
objFile.Close
Set objFile = objFSO.OpenTextFile("H:\Letter Display\Letters\LTRPRT__00000008720000000001NI-K-RMND.txt", ForWriting)
objFile.Write strText
objFile.Close
If anyone could help with making this an IF statement it would be much appreciated.
You need to move the ReadLine and the conditional inside the Do..Loop:
strNIK = "1000"
arrCommas = Array(16,31,46,56,66,79,94,99)
Do Until objFile.AtEndOfStream
strLine = objFile.ReadLine
If Left(strLine, 4) = strNIK then
intLength = Len(strLine)
For Each strComma in arrCommas
strLine = Left(strLine, strComma - 1) + "," _
+ Mid(strLine, strComma, intLength)
Next
End If
strText = strText & strLine & vbCrLf
Loop
If you want the output to consist only of the modified lines, move the line strText = strText & strLine & vbCrLf inside the conditional:
If Left(strLine, 4) = strNIK then
'...
strText = strText & strLine & vbCrLf
End If
Do the comma indexes in your array already account for the positional shift that is caused by the character insertion?
Also, it might be a good idea to write the output line by line to a temporary file and then replace the input file with that temporary file after you finished processing all input:
Set inFile = objFSO.OpenTextFile(inputFilename, ForReading)
Set outFile = objFSO.OpenTextFile(outputFilename, ForWriting)
Do Until inFile.AtEndOfStream
strLine = inFile.ReadLine
'do stuff with strLine
outFile.WritLine
Loop
inFile.Close
outFile.Close
objFSO.DeleteFile inputFilename, True
objFSO.MoveFile outputFilename, inputFilename
That way you can avoid memory exhaustion when processing large files.
You can process all files with a specific extension in a given directory like this:
folderName = "C:\some\folder"
For Each objFile In objFSO.GetFolder(folderName).Files
If LCase(objFSO.GetExtensionName(objFile.Name)) = "ltr" Then
'processing takes place here
End If
Next
If you want to use the inputfile/outputfile approach I suggested above, you can either use the same temporary outputfile name for each input file, or you can derive the outputfile name from the inputfile name, e.g. like this:
inputFilename = objFile.Name
outputFilename = inputFilename & ".tmp"
Related
I have written a VB script to count the number of rows from several CSV files and save in a File in FileRecordCount.csv file with the filename and number of rows. The code does not show any errors at all.
But the script is not working at all. I don't know what is the problem. Any help Any help would be greatly appreciated!????
Const ForReading = 1
Const ForWriting = 2
Set objFSO = CreateObject("Scripting.FileSystemObject")
vbsFile = Wscript.ScriptName
vbsFilePath = Wscript.ScriptFullName
sFolder = left(vbsFilePath,len(vbsFilePath)-(len(vbsFile)+1))
ResultsFile = sFolder & "\FileRecordCount.csv"
For Each objFile In objFSO.GetFolder(sFolder).Files
If UCase(objFSO.GetExtensionName(objFile.Name)) = "CSV" Then
oFile = objFile.path
Set objFile = objFSO.OpenTextFile(oFile, ForReading)
RowCount = 0
Do Until objFile.AtEndOfStream
WScript.Echo objFile.ReadLine
RowCount = RowCount + 1
Loop
'need to make a new file to write the results to
FileName = objFSO.GetFileName(ResultsFile)
strText = FileName & "," & RowCount
Set objFile = objFSO.OpenTextFile(ResultsFile, ForWriting)
objFile.WriteLine strText
objFile.Close
End if
Next
MsgBox "FileRowCount Complete"
The script has multiple issues. First, as #Flakes stated, the same objFile variable is used for the files being processed and the results file. Second, the results filename is being written to the results file instead of each processed file name. Additionally, the variable "oFile" is a string, so would be better named "strFile" and the script path directory can be derived a bit more elegantly. Also, as per #Flakes, I moved the results file open before the loop and the results file close after the loop and I named the results file with a .txt extension so it doesn't get processed as one of the CSV files. Also added a Create = True flag to the results file open, so it doesn't have to pre-exist.
Here is the edited script:
Const ForReading = 1
Const ForWriting = 2
Const Create = True
Set objFSO = CreateObject("Scripting.FileSystemObject")
sFolder = objFSO.GetParentFolderName(WScript.ScriptFullName)
ResultsFile = sFolder & "\FileRecordCount.txt"
Set objResultsFile = objFSO.OpenTextFile(ResultsFile, ForWriting, Create)
For Each objFile In objFSO.GetFolder(sFolder).Files
If UCase(objFSO.GetExtensionName(objFile.Name)) = "CSV" Then
strFile = objFile.path
Set objReadFile = objFSO.OpenTextFile(strFile, ForReading)
RowCount = 0
FirstLine = True
Do Until objReadFile.AtEndOfStream
Line = objReadFile.ReadLine
RowCount = RowCount + 1
If FirstLine Then ColumnCount = UBound(Split(Line,",")) + 1
FirstLine = False
Loop
objReadFile.Close
strText = strFile & "," & RowCount & "," & ColumnCount
objResultsFile.WriteLine strText
End if
Next
objResultsFile.Close
WScript.Echo "File row/column count complete"
I am trying to get data from a subform into word, if there is more that 1 row of data(eg 1st row = 3 cats, 2nd row = 1 dog (Me![pets_Information]![PetType]) ) I can only get the 3 cats to copy to word, I am importing to Legacy Forms - Text Form Field.
What I need to achieve is :- 3 Cats, 1 Dog in the one text field
There seems to be very little of this that I can find on the internet, always finding just from the main form and nothing really regarding subform/childforms
There are 3 tables that I need to set this up for all have their own keyID's
Function FillLetter()
Dim appword As Word.Application
Dim doc As Word.Document
Dim path As String
On Error Resume Next
Err.Clear
''''''Chaange for which computer''''''''''''''
path = "F:\Access Stuff\Job for John - PSA\Homestay Provider Information.docx"
'path = "G:\Access Stuff\Job for John - PSA\Homestay Provider Information.docx"
Set appword = GetObject(, "word.application")
If Err.Number <> 0 Then
Set appword = New Word.Application
appword.Visible = True
End If
Set doc = appword.Documents.Open(path, , True)
With doc
.FormFields("txtClientsFName").Result = (Me.Title) & " " & (Me!ClientFirstName) & " " & (Me!ClientFamilyName) '''works
.FormFields("txtAddress").Result = (Me!Address) '''works
.FormFields("txtSuburb").Result = (Me!Suburb) & ", WA " & (Me.PostCode) '''works
.FormFields("txtContactType2").Result = (Me![Contact_Information]![ContactType]) & " " & (Me![Contact_Information]![ContactDetails])
.FormFields("txtFamily").Result = (Me![Family_Information]![Relationship]) & " " & (Me![Family_Information]![Age])
.FormFields("txtPolice").Result = Me!LegalCert '''works
.FormFields("txtCosts").Result = Me!CPW '''works
.FormFields("txtMeals").Result = Me.IEMeals '''works
.FormFields("txtPets").Result = (Me![Pets_Infomation]![PetType])
.FormFields("txtHobbies").Result = Me!HobbiesInterests '''works
.FormFields("txtInstitute").Result = Me.Institution '''works
.FormFields("txtTravel").Result = Me.ToUniCollege '''works
.FormFields("txtOther").Result = Me!OtherInformation '''works
.Visible = True
.Activate
End With
Set doc = Nothing
Set appword = Nothing
End Function
You can use RecordsetClone to get the underlying subform data
Add this function to your Form (make sure constants match your subform/field):
Private Function GetPetTypes() As String
Const SUBFORM_NAME As String = "Pets_Infomation"
Const PET_TYPEFIELD As String = "PetType"
Dim strPetList As String
With Me(SUBFORM_NAME).Form.RecordsetClone
If .RecordCount > 0 Then
' Start with first record
.MoveFirst
Do While Not .EOF
If strPetList <> "" Then
strPetList = strPetList & ","
End If
strPetList = strPetList & .Fields(PET_TYPEFIELD)
.MoveNext
Loop
' Go Back to first record in case it needs to be reused
.MoveFirst
End If
End With
GetPetTypes = strPetList
End Function
Then replace this line:
.FormFields("txtPets").Result = (Me![Pets_Infomation]![PetType])
with this line
.FormFields("txtPets").Result = GetPetTypes()
I've compiled a script that adds a trailer to the end of a .csv file (to let the program reading it know that it's finished transmitting data.) Anyways, I'm trying to figure out how to only add this if the file that's referenced contains data.
dim filesys, filetxt
Const ForReading = 1, ForWriting = 2, ForAppending = 8
Set filesys = CreateObject("Scripting.FileSystemObject")
Set filetxt = filesys.OpenTextFile("C:\Test\Test.csv", ForAppending, True)
filetxt.WriteLine(",,,,,,,,,,,," & vbCr)
filetxt.WriteLine(",,,,,,,,,,,," & vbCr)
filetxt.WriteLine(",,,,,,,,,,,," & vbCr)
filetxt.WriteLine(",,,,,,,,,,,," & vbCr)
filetxt.WriteLine(",,,,,,,,,,,," & vbCr)
filetxt.Close
Any help is appreciated!
Try to check the size of the file:
strFile = "C:\file.txt"
SET objFSO = CREATEOBJECT("Scripting.FileSystemObject")
SET objFile = objFSO.GetFile(strFile)
If objFile.Size > 0 Then
'do things here
Else
'do nothing
End If
I have a bunch of text files that I need to import into MS Access (thousands) - can use 2007 or 2010. The text files have categories that are identified in square brackets and have relevant data between the categories - for example:
[Location]Tenessee[Location][Model]042200[Model][PartNo]113342A69447B6[PartNo].
I need to capture both the categories and the data between them and import them into Access - the categories to one table, the data to another. There are hundreds of these categories in a single file and the text file has no structure - they are all run together as in the example above. The categories in the brackets are the only clear delimiters.
Through research on the web I have come up with a script for VBS (I am not locked into VBS, willing to use VBA or another method), but when I run it, I am getting a VBS info window with nothing displaying in it. Any advice or guidance would be most gratefully appreciated (I do not tend to use VBS and VBA) and I thank you.
The Script:
Const ForReading = 1
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile("C:\Users\testGuy\Documents\dmc_db_test\DMC-TEST-A-00-00-00-00A-022A-D_000 - Copy01.txt", ForReading)
strContents = objFile.ReadAll
objFile.Close
Set objRegEx = CreateObject("VBScript.RegExp")
objRegEx.Global = True
objRegEx.Pattern = "\[.{0,}\]"
Set colMatches = objRegEx.Execute(strContents)
If colMatches.Count > 0 Then
For Each strMatch in colMatches
strMatches = strMatches & strMatch.Value
Next
End If
strMatches = Replace(strMatches, "]", vbCrlf)
strMatches = Replace(strMatches, "[", "")
Wscript.Echo strMatches
Regular expressions are wonderful things, but in your case it looks like they might be overkill. The following code uses plain old InStr() to find the [Tags] and parses the file(s) out to a single CSV file. That is, for input files
testfile1.txt:
[Location]Tennessee[Location][Model]042200[Model][PartNo]113342A69447B6[PartNo]
[Location]Mississippi[Location][Model]042200[Model][SerialNo]3212333222355[SerialNo]
and testfile2.txt:
[Location]Missouri[Location][Model]042200[Model][PartNo]AAABBBCCC111222333[PartNo]
...the code will write the following output file...
"FileName","LineNumber","ItemNumber","FieldName","FieldValue"
"testfile1.txt",1,1,"Location","Tennessee"
"testfile1.txt",1,2,"Model","042200"
"testfile1.txt",1,3,"PartNo","113342A69447B6"
"testfile1.txt",2,1,"Location","Mississippi"
"testfile1.txt",2,2,"Model","042200"
"testfile1.txt",2,3,"SerialNo","3212333222355"
"testfile2.txt",1,1,"Location","Missouri"
"testfile2.txt",1,2,"Model","042200"
"testfile2.txt",1,3,"PartNo","AAABBBCCC111222333"
...which you can then import into Access (or whatever) and proceed from there. This is VBA code, but it could easily be tweaked to run as a VBScript.
Sub ParseSomeFiles()
Const InFolder = "C:\__tmp\parse\in\"
Const OutFile = "C:\__tmp\parse\out.csv"
Dim fso As FileSystemObject, f As File, tsIn As TextStream, tsOut As TextStream
Dim s As String, Lines As Long, Items As Long, i As Long
Set fso = New FileSystemObject
Set tsOut = fso.CreateTextFile(OutFile, True)
tsOut.WriteLine """FileName"",""LineNumber"",""ItemNumber"",""FieldName"",""FieldValue"""
For Each f In fso.GetFolder(InFolder).Files
Debug.Print "Parsing """ & f.Name & """..."
Set tsIn = f.OpenAsTextStream(ForReading)
Lines = 0
Do While Not tsIn.AtEndOfStream
s = Trim(tsIn.ReadLine)
Lines = Lines + 1
Items = 0
Do While Len(s) > 0
Items = Items + 1
tsOut.Write """" & f.Name & """," & Lines & "," & Items
i = InStr(1, s, "]", vbBinaryCompare)
' write out FieldName
tsOut.Write ",""" & Replace(Mid(s, 2, i - 2), """", """""", 1, -1, vbBinaryCompare) & """"
s = Mid(s, i + 1)
i = InStr(1, s, "[", vbBinaryCompare)
' write out FieldValue
tsOut.Write ",""" & Replace(Mid(s, 1, i - 1), """", """""", 1, -1, vbBinaryCompare) & """"
s = Mid(s, i)
i = InStr(1, s, "]", vbBinaryCompare)
' (no need to write out ending FieldName tag)
s = Mid(s, i + 1)
tsOut.WriteLine
Loop
Loop
tsIn.Close
Set tsIn = Nothing
Next
Set f = Nothing
tsOut.Close
Set tsOut = Nothing
Set fso = Nothing
Debug.Print "Done."
End Sub
Okay, so I want to have a macro in Excel 2003 which saves the current worksheet as a .txt file. I've already got that part with the following code:
Dim filename As String
Dim path As String
filename = InputBox("Please enter file name", "Save as CSV", "CSV_" & Format(Now, "DD_MM_yyyy"))
path = "C:\Temp" & filename & ".txt"
ActiveWorkbook.SaveAs filename:=path, FileFormat:=xlTextMSDOS, CreateBackup:=False
But now to the actual problem: In my sheet there are some cells which contain a comma. If I use the macro shown above, the file gets saved as CSV, but the cells containing a comma have quotation marks around them. I do not want that.
If I save the file manually via File -> Save as -> CSV/TXT, the resulting file does not contain these quotation marks.
Does anyone know how to solve this problem?
Many thanks!
Edit: I forgot to say that, when saving manually, I select Text tab-seperated, and not comma-seperated.
OK, Let's see what I've got in the attic...
I have a VBA Array To File function which fits the bill: probably overkill for the work you're doing, as you don't need the options for header rows, transposing, and checking for pre-existing files with an error-trap that reads the file's datestamp and prevents repeated calls to the function continually overwriting the file. But it's the code I've got to hand, and simplifying it is more trouble than using it as-is.
The thing you do want is that this function uses the Tab character as a field delimiter by default. You could, of course, set it to the comma... The commonly-accepted definition of csv file is fields delimited by commas and text fields (which may contain the comma character) encapsulated in double-quotes. But I can't claim the moral high ground that would justify this kind of pedantry, because the code below doesn't impose the encapsulating quotes.
Coding Notes:
You need a reference to the Windows Scripting Runtime Library: scrrun.dll - this can be found in the system folder (usually C:\WINDOWS\system32) - as we're using the File System Object;
ArrayToFile writes the data to your named file in the temp folder. If you specify 'CopyFilePath', this will be copied elsewhere: never write to a network folder, it's always faster to write to a local drive and use the native file system functions to move or copy the finished file;
Data is written to the file in blocks, instead of line-by-line;
There is scope for further optimisation: using Split and Join functions would eliminate the string concatenations in the loops;
You might want to use VbCrLF as a row delimiter instead of VbCr: carriage returns usually work but some systems and applications need the Carriage-Return-and-LineFeed combination in order to read or display line breaks correctly.
Using the ArrayToFile function:
This is easy: just feed in the .Value2 property of the sheet's used range:
ArrayToFile Worksheets("Sheet1").UsedRange.Value2, "MyData.csv"
The reason for 'Value2' is that the 'Value' property captures formatting, and you probably want the underlying serial values of date fields.
Source code for the VBA ArrayToFile function:
Share and Enjoy... And watch out for helpful line breaks, inserted wherever they can break the code by your browser (or by StackOverflow's helpful formatting functions):
Public Sub ArrayToFile(ByVal arrData As Variant, _
ByVal strName As String, _
Optional MinFileAge As Double = 0, _
Optional Transpose As Boolean = False, _
Optional RowDelimiter As String = vbCr, _
Optional FieldDelimiter = vbTab, _
Optional CopyFilePath As String, _
Optional NoEmptyRows As Boolean = True, _
Optional arrHeader1 As Variant, _
Optional arrHeader2 As Variant)
' Output an array to a file. The field delimiter is tab (char 9); rows use CarriageReturn(char 13).
' The file will be named as specified by strName, and saved in the user's Windows Temp folder.
' Specify CopyFilePath (the full name and path) to copy this temporary file to another folder.
' Saving files locally and copying them is much faster than writing data across the network.
' If a Min File Age 'n' is specified, and n is greater than zero, an existing file will not be
' replaced, and no data will be written unless the file is more than MinFileAge seconds old.
' Transpose = TRUE is useful for arrays generated by Recordset.GetRows and ListControl.Column
' Note that ADODB.Recordset has a native 'save' method (rows delimited by VbCr, fields by Tab)
' **** This code is in the Public Domain **** Nigel Heffernan http://Excellerando.blogspot.com
On Error Resume Next
Dim objFSO As Scripting.FileSystemObject
Set objFSO = New Scripting.FileSystemObject
If objFSO Is Nothing Then
Shell "Regsvr32.exe /s scrrun.dll", vbHide
Application.Wait Now + (0.25 / 3600 / 24)
Set objFSO = CreateObject("Scripting.FileSystemObject")
End If
If objFSO Is Nothing Then
Exit Sub
End If
Dim strFile As String
Dim strTemp As String
Dim i As Long, j As Long
Dim strData As String
Dim strLine As String
Dim strEmpty As String
Dim dblCount As Double
Const BUFFERLEN As Long = 255
strName = Replace(strName, "[", "")
strName = Replace(strName, "]", "")
Set objFSO = New Scripting.FileSystemObject
If objFSO Is Nothing Then
Shell "Regsvr32.exe /s scrrun.dll", vbHide
Application.Wait Now + (0.25 / 3600 / 24)
Set objFSO = CreateObject("Scripting.FileSystemObject")
End If
If objFSO Is Nothing Then
Exit Sub
End If
strTemp = objFSO.GetSpecialFolder(Scripting.TemporaryFolder).ShortPath
strFile = objFSO.BuildPath(strTemp, strName)
If objFSO.FileExists(strFile) Then
If MinFileAge > 0 Then
If objFSO.GetFile(strFile).DateCreated + (MinFileAge / 3600 / 24) > Now Then
Set objFSO = Nothing
Exit Sub
End If
End If
Err.Clear
objFSO.DeleteFile strFile, True
If Err.Number = 70 Then
VBA.FileSystem.Kill strFile
End If
End If
If objFSO.FileExists(strFile) Then
Exit Sub
End If
Application.StatusBar = "Cacheing data in a temp file... "
strData = vbNullString
With objFSO.OpenTextFile(strFile, ForWriting, True)
' **** **** **** HEADER1 **** **** ****
If Not IsMissing(arrHeader1) Then
If Not IsEmpty(arrHeader1) Then
If InStr(1, TypeName(arrHeader1), "(") > 1 Then ' It's an array...
Select Case ArrayDimensions(arrHeader1)
Case 1 ' Vector array
.Write Join(arrHeader1, RowDelimiter)
Case 2 ' 2-D array... 3-D arrays are not handled
If Transpose = True Then
For i = LBound(arrHeader1, 2) To UBound(arrHeader1, 2)
For j = LBound(arrHeader1, 1) To UBound(arrHeader1, 1)
strData = strData & FieldDelimiter & CStr(arrHeader1(j, i))
Next j
strData = strData & RowDelimiter
Next i
Else ' not transposing:
For i = LBound(arrHeader1, 1) To UBound(arrHeader1, 1)
For j = LBound(arrHeader1, 2) To UBound(arrHeader1, 2)
strData = strData & CStr(arrHeader1(i, j))
If j < UBound(arrHeader1, 2) Then
strData = strData & FieldDelimiter
End If
Next j
strData = strData & RowDelimiter
Next i
End If ' Transpose
End Select
' .Write strData
' strData = vbNullString
Erase arrHeader1
Else ' treat it as a string
If LenB(arrHeader1) > 0 Then
.Write arrHeader1
End If
End If
End If 'Not IsMissing(arrHeader1)
End If 'Not IsEmpty(arrHeader1)
' **** **** **** HEADER2 **** **** ****
If Not IsMissing(arrHeader2) Then
If Not IsEmpty(arrHeader2) Then
If InStr(1, TypeName(arrHeader2), "(") > 1 Then ' It's an array...
Select Case ArrayDimensions(arrHeader2)
Case 1 ' Vector array
.Write Join(arrHeader2, RowDelimiter)
Case 2 ' 2-D array... 3-D arrays are not handled
If Transpose = True Then
For i = LBound(arrHeader2, 2) To UBound(arrHeader2, 2)
For j = LBound(arrHeader2, 1) To UBound(arrHeader2, 1)
strData = strData & FieldDelimiter & CStr(arrHeader2(j, i))
Next j
strData = strData & RowDelimiter
Next i
Else ' not transposing:
For i = LBound(arrHeader2, 1) To UBound(arrHeader2, 1)
For j = LBound(arrHeader2, 2) To UBound(arrHeader2, 2)
strData = strData & CStr(arrHeader2(i, j))
If j < UBound(arrHeader2, 2) Then
strData = strData & FieldDelimiter
End If
Next j
strData = strData & RowDelimiter
Next i
End If ' Transpose
End Select
' .Write strData
' strData = vbNullString
Erase arrHeader2
Else ' treat it as a string
If LenB(arrHeader2) > 0 Then
.Write arrHeader2
End If
End If
End If 'Not IsMissing(arrHeader2)
End If 'Not IsEmpty(arrHeader2)
' **** **** **** BODY **** **** ****
If InStr(1, TypeName(arrData), "(") > 1 Then
' It's an array...
Select Case ArrayDimensions(arrData)
Case 1
If NoEmptyRows Then
.Write Replace$(Join(arrData, RowDelimiter), RowDelimiter & RowDelimiter, "")
Else
.Write Join(arrData, RowDelimiter)
End If
Case 2
If Transpose = True Then
strEmpty = String(UBound(arrData, 1) - 1, FieldDelimiter) & RowDelimiter
For i = LBound(arrData, 2) To UBound(arrData, 2)
For j = LBound(arrData, 1) To UBound(arrData, 1)
strData = strData & FieldDelimiter & CStr(arrData(j, i))
Next j
strData = strData & RowDelimiter
If (Len(strData) \ 1024) > BUFFERLEN Then
If NoEmptyRows Then
strData = Replace$(strData, strEmpty, "")
'strData = Replace$(strData, RowDelimiter & RowDelimiter, "")
End If
Application.StatusBar = "Cacheing data in a temp file... (" & Format(dblCount + (Len(strData) \ 1024), "0,000") & "kB)"
dblCount = dblCount + (Len(strData) \ 1024)
.Write strData
strData = vbNullString
End If
Next i
Else ' not transposing:
strEmpty = String(UBound(arrData, 2) - 1, FieldDelimiter) & RowDelimiter
For i = LBound(arrData, 1) To UBound(arrData, 1)
For j = LBound(arrData, 2) To UBound(arrData, 2)
strData = strData & CStr(arrData(i, j))
If j < UBound(arrData, 2) Then
strData = strData & FieldDelimiter
End If
Next j
strData = strData & RowDelimiter
If (Len(strData) \ 1024) > BUFFERLEN Then
If NoEmptyRows Then
strData = Replace$(strData, strEmpty, "")
'strData = Replace$(strData, RowDelimiter & RowDelimiter, "")
End If
Application.StatusBar = "Cacheing data in a temp file... (" & Format(dblCount + (Len(strData) \ 1024), "0,000") & "kB)"
dblCount = dblCount + (Len(strData) \ 1024)
.Write strData
strData = vbNullString
End If
Next i
End If ' Transpose
End Select
If NoEmptyRows Then
strData = Replace$(strData, strEmpty, "")
'strData = Replace$(strData, RowDelimiter & RowDelimiter, "")
End If
If Right$(strData, Len(RowDelimiter)) = RowDelimiter Then
Mid$(strData, Len(strData) - Len(RowDelimiter), Len(RowDelimiter)) = ""
End If
.Write strData
strData = vbNullString
Erase arrData
Else ' treat it as a string
.Write arrData
End If
.Close
End With ' textstream object from objFSO.OpenTextFile
If CopyFilePath <> "" Then
Application.StatusBar = "Copying " & strName & " to " & CopyFilePath & "..."
objFSO.CopyFile strFile, CopyFilePath, True
End If
Application.StatusBar = False
Set objFSO = Nothing
strData = vbNullString
End Sub
For completeness, here's the complementary function that reads from files into an array, and a rough-and-ready subroutine to clean up your temp files:
Public Sub FileToArray(arrData As Variant, strName As String, Optional MaxFileAge As Double = 0, Optional RowDelimiter As String = vbCr, Optional FieldDelimiter = vbTab, Optional CoerceLowerBound As Long = 0) ' 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
' If a Max File Age 'n' is specified, and n is greater than zero, files more than n seconds old will NOT be read.
' **** This code is in the Public Domain **** Nigel Heffernan http://Excellerando.blogspot.com
On Error Resume Next
Dim objFSO As Scripting.FileSystemObject
Set objFSO = New Scripting.FileSystemObject
If objFSO Is Nothing Then
Shell "Regsvr32.exe /s scrrun.dll", vbHide
Application.Wait Now + (0.25 / 3600 / 24)
Set objFSO = CreateObject("Scripting.FileSystemObject")
End If
If objFSO Is Nothing Then
Exit Sub
End If
Dim strFile As String
Dim strTemp As String
Dim i As Long
Dim j As Long
Dim i_n As Long
Dim j_n As Long
Dim i_lBound As Long
Dim i_uBound As Long
Dim j_lBound As Long
Dim j_uBound As Long
Dim arrTemp1 As Variant
Dim arrTemp2 As Variant
Dim dblCount As Double
Set objFSO = New Scripting.FileSystemObject
If objFSO Is Nothing Then
Shell "Regsvr32.exe /s scrrun.dll", vbHide
Application.Wait Now + (0.25 / 3600 / 24)
Set objFSO = CreateObject("Scripting.FileSystemObject")
End If
If objFSO Is Nothing Then
Exit Sub
End If
strTemp = objFSO.GetSpecialFolder(Scripting.TemporaryFolder).ShortPath
strFile = objFSO.BuildPath(strTemp, strName)
If Not objFSO.FileExists(strFile) Then
Exit Sub
End If
If MaxFileAge > 0 Then
' If the file's a bit elderly, bail out - the calling function will refresh the data from source
If objFSO.GetFile(strFile).DateCreated + (MaxFileAge / 3600 / 24) < Now Then
Set objFSO = Nothing
Exit Sub
End If
End If
Application.StatusBar = "Reading the file... (" & strName & ")"
arrData = Split2d(objFSO.OpenTextFile(strFile, ForReading).ReadAll, RowDelimiter, FieldDelimiter, CoerceLowerBound)
Application.StatusBar = "Reading the file... Done"
Set objFSO = Nothing
End Sub
Public Sub RemoveTempFiles(ParamArray FileNames())
On Error Resume Next
Dim objFSO As Scripting.FileSystemObject
Set objFSO = New Scripting.FileSystemObject
If objFSO Is Nothing Then
Shell "Regsvr32.exe /s scrrun.dll", vbHide
Application.Wait Now + (0.25 / 3600 / 24)
Set objFSO = CreateObject("Scripting.FileSystemObject")
End If
If objFSO Is Nothing Then
Exit Sub
End If
Dim varName As Variant
Dim strName As String
Dim strFile As String
Dim strTemp As String
strTemp = objFSO.GetSpecialFolder(Scripting.TemporaryFolder).ShortPath
For Each varName In FileNames
strName = vbNullString
strFile = vbNullString
strName = CStr(varName)
strFile = objFSO.BuildPath(strTemp, strName)
If objFSO.FileExists(strFile) Then
objFSO.DeleteFile strFile, True
End If
Next varName
Set objFSO = Nothing
End Sub
I'd advise you to keep this in a module under Option Private Module - this isn't the kind of function I'd want other users calling from a worksheet directly.
This is impossible (sort of).
A field that contains the delimiter must be enclosed in quotes. Otherwise, that field would be "torn in two" by the delimiter.
The only solution is to use a different delimiter, for example tabs (effectively changing it to a TSV file), which of course only works if that new delimiter doesn't occur in the data either.
If none of the SaveAs formats work for you, write your parser, eg
Sub SaveFile()
Dim rng As Range
Dim rw As Range
Dim ln As Variant
' Set rng to yout data range, eg
Set rng = ActiveSheet.UsedRange
Open "C:\Temp\TESTFILE.txt" For Output As #1 ' Open file for output.
For Each rw In rng.Rows
ln = Join(Application.Transpose(Application.Transpose(rw)), vbTab)
Print #1, ln; vbNewLine;
Next
Close #1
End Sub